Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e521914f0f | ||
|
|
7cb469ff75 | ||
|
|
9118e7b4cb | ||
|
|
841a66f375 | ||
|
|
eb06adb2be | ||
|
|
3913788fd8 | ||
|
|
6ea02eb023 | ||
|
|
55154384f6 | ||
|
|
19e4e2d635 | ||
|
|
8f2396f04a | ||
|
|
bffc0ee0d0 |
@@ -102,18 +102,7 @@ jobs:
|
||||
|
||||
- name: Publish to npm
|
||||
working-directory: ./hindsight-clients/typescript
|
||||
run: |
|
||||
set +e
|
||||
OUTPUT=$(npm publish --access public 2>&1)
|
||||
EXIT_CODE=$?
|
||||
echo "$OUTPUT"
|
||||
if [ $EXIT_CODE -ne 0 ]; then
|
||||
if echo "$OUTPUT" | grep -q "cannot publish over"; then
|
||||
echo "Package version already published, skipping..."
|
||||
exit 0
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
run: npm publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
@@ -128,65 +117,6 @@ jobs:
|
||||
path: hindsight-clients/typescript/*.tgz
|
||||
retention-days: 1
|
||||
|
||||
release-control-plane:
|
||||
runs-on: ubuntu-latest
|
||||
environment: npm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build TypeScript client (dependency)
|
||||
run: npm run build --workspace=hindsight-clients/typescript
|
||||
|
||||
- name: Fix platform-specific native modules
|
||||
run: |
|
||||
# npm ci installs from lockfile which may have wrong platform binaries
|
||||
# Delete hoisted native modules and reinstall for current platform
|
||||
rm -rf node_modules/lightningcss node_modules/@tailwindcss
|
||||
npm install lightningcss @tailwindcss/postcss @tailwindcss/node
|
||||
|
||||
- name: Build
|
||||
run: npm run build --workspace=hindsight-control-plane
|
||||
|
||||
- name: Publish to npm
|
||||
working-directory: ./hindsight-control-plane
|
||||
run: |
|
||||
set +e
|
||||
OUTPUT=$(npm publish --access public 2>&1)
|
||||
EXIT_CODE=$?
|
||||
echo "$OUTPUT"
|
||||
if [ $EXIT_CODE -ne 0 ]; then
|
||||
if echo "$OUTPUT" | grep -q "cannot publish over"; then
|
||||
echo "Package version already published, skipping..."
|
||||
exit 0
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Pack for GitHub release
|
||||
working-directory: ./hindsight-control-plane
|
||||
run: npm pack
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: control-plane
|
||||
path: hindsight-control-plane/*.tgz
|
||||
retention-days: 1
|
||||
|
||||
release-rust-cli:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
@@ -251,7 +181,7 @@ jobs:
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
tool-cache: true
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
@@ -276,7 +206,7 @@ jobs:
|
||||
id: get_version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract metadata for release tags
|
||||
- name: Extract metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
@@ -287,29 +217,7 @@ jobs:
|
||||
type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }}
|
||||
type=raw,value=latest
|
||||
|
||||
# TODO: Re-enable smoke test when disk space issue is resolved
|
||||
# # Step 1: Build for local testing (single platform, no push)
|
||||
# # This creates an identical image to what will be released, just for one platform
|
||||
# - name: Build image for testing
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# context: .
|
||||
# file: docker/standalone/Dockerfile
|
||||
# target: ${{ matrix.target }}
|
||||
# push: false
|
||||
# load: true
|
||||
# tags: ${{ matrix.image_name }}:test
|
||||
# cache-from: type=gha
|
||||
# cache-to: type=gha,mode=max
|
||||
|
||||
# # Step 2: Test the image before pushing anything
|
||||
# - name: Smoke test - verify container starts
|
||||
# env:
|
||||
# GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
# run: ./scripts/docker-smoke-test.sh "${{ matrix.image_name }}:test" "${{ matrix.target }}"
|
||||
|
||||
# Build multi-platform and push to release tags
|
||||
- name: Build and push release images
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
@@ -355,7 +263,7 @@ jobs:
|
||||
|
||||
create-github-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [release-python-packages, release-typescript-client, release-control-plane, release-rust-cli, release-docker-images, release-helm-chart]
|
||||
needs: [release-python-packages, release-typescript-client, release-rust-cli, release-docker-images, release-helm-chart]
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -378,12 +286,6 @@ jobs:
|
||||
name: typescript-client
|
||||
path: ./artifacts/typescript-client
|
||||
|
||||
- name: Download Control Plane
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: control-plane
|
||||
path: ./artifacts/control-plane
|
||||
|
||||
- name: Download Rust CLI (Linux)
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -418,8 +320,6 @@ jobs:
|
||||
cp artifacts/python-packages/hindsight-integrations/litellm/dist/* release-assets/ || true
|
||||
# TypeScript client
|
||||
cp artifacts/typescript-client/*.tgz release-assets/ || true
|
||||
# Control Plane
|
||||
cp artifacts/control-plane/*.tgz release-assets/ || true
|
||||
# Rust CLI binaries
|
||||
cp artifacts/rust-cli-linux/hindsight-linux-amd64 release-assets/ || true
|
||||
cp artifacts/rust-cli-darwin-amd64/hindsight-darwin-amd64 release-assets/ || true
|
||||
|
||||
@@ -80,58 +80,6 @@ jobs:
|
||||
- name: Build TypeScript client
|
||||
run: npm run build --workspace=hindsight-clients/typescript
|
||||
|
||||
build-control-plane:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: package-lock.json
|
||||
|
||||
- name: Install SDK dependencies
|
||||
run: npm ci --workspace=hindsight-clients/typescript
|
||||
|
||||
- name: Build SDK
|
||||
run: npm run build --workspace=hindsight-clients/typescript
|
||||
|
||||
# Install control plane deps and fix hoisted lightningcss binary
|
||||
# lightningcss gets hoisted to root node_modules, so we need to reinstall it there
|
||||
- name: Install Control Plane dependencies
|
||||
run: |
|
||||
npm install --workspace=hindsight-control-plane
|
||||
rm -rf node_modules/lightningcss node_modules/@tailwindcss
|
||||
npm install lightningcss @tailwindcss/postcss @tailwindcss/node
|
||||
|
||||
- name: Build Control Plane
|
||||
run: npm run build --workspace=hindsight-control-plane
|
||||
|
||||
- name: Verify standalone build
|
||||
run: |
|
||||
test -f hindsight-control-plane/standalone/server.js || exit 1
|
||||
test -d hindsight-control-plane/standalone/node_modules || exit 1
|
||||
node hindsight-control-plane/bin/cli.js --help
|
||||
|
||||
- name: Smoke test - verify server starts
|
||||
run: |
|
||||
cd hindsight-control-plane
|
||||
node bin/cli.js --port 9999 &
|
||||
SERVER_PID=$!
|
||||
sleep 5
|
||||
if curl -sf http://localhost:9999 > /dev/null 2>&1; then
|
||||
echo "Server started successfully"
|
||||
kill $SERVER_PID 2>/dev/null || true
|
||||
exit 0
|
||||
else
|
||||
echo "Server failed to respond"
|
||||
kill $SERVER_PID 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -205,7 +153,7 @@ jobs:
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
tool-cache: true
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
@@ -223,13 +171,6 @@ jobs:
|
||||
file: docker/standalone/Dockerfile
|
||||
target: ${{ matrix.target }}
|
||||
push: false
|
||||
load: false
|
||||
|
||||
# TODO: Re-enable smoke test when disk space issue is resolved
|
||||
# - name: Smoke test - verify container starts
|
||||
# env:
|
||||
# GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
||||
# run: ./scripts/docker-smoke-test.sh "hindsight-${{ matrix.name }}:test" "${{ matrix.target }}"
|
||||
|
||||
test-api:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -72,48 +72,30 @@ FROM node:20-slim AS cp-builder
|
||||
ARG INCLUDE_CP
|
||||
RUN if [ "$INCLUDE_CP" != "true" ]; then echo "Skipping CP build" && exit 0; fi
|
||||
|
||||
# Create directory structure matching the monorepo layout
|
||||
# This is required because build:standalone script expects .next/standalone/memory-poc/hindsight-control-plane
|
||||
WORKDIR /app/memory-poc/hindsight-control-plane
|
||||
WORKDIR /app
|
||||
|
||||
# Copy built SDK
|
||||
COPY --from=sdk-builder /app/hindsight-clients/typescript /app/sdk
|
||||
|
||||
# Install Control Plane dependencies
|
||||
# Only copy package.json (not package-lock.json) to ensure npm installs
|
||||
# correct platform-specific native bindings for lightningcss/tailwindcss
|
||||
COPY hindsight-control-plane/package.json ./
|
||||
# Remove the file: dependency on SDK (we'll copy it directly later)
|
||||
RUN sed -i '/"@vectorize-io\/hindsight-client":/d' package.json
|
||||
RUN npm install
|
||||
|
||||
# Copy Control Plane source (excluding node_modules via .dockerignore)
|
||||
COPY hindsight-control-plane/ ./
|
||||
# Remove package-lock.json to avoid conflicts with installed native bindings
|
||||
# Also remove the file: dependency from package.json (restored by COPY above)
|
||||
RUN rm -f package-lock.json && sed -i '/"@vectorize-io\/hindsight-client":/d' package.json
|
||||
RUN rm -f package-lock.json
|
||||
|
||||
# Copy built SDK directly into node_modules (more reliable than npm link in Docker)
|
||||
COPY --from=sdk-builder /app/hindsight-clients/typescript ./node_modules/@vectorize-io/hindsight-client
|
||||
# Link SDK (temporary for build)
|
||||
RUN cd /app/sdk && npm link && cd /app && npm link @vectorize-io/hindsight-client
|
||||
|
||||
# Build Control Plane - run next build first, then custom standalone copy
|
||||
# (The build:standalone script expects a specific path structure that differs in Docker)
|
||||
RUN npm exec -- next build
|
||||
# Build Control Plane
|
||||
RUN npm run build
|
||||
|
||||
# Create standalone directory structure manually
|
||||
# Note: Must exclude node_modules from find to avoid wrong server.js from next/dist/experimental/testmode/
|
||||
# Note: Must explicitly copy .next since glob * doesn't match hidden directories
|
||||
RUN STANDALONE_ROOT=$(find .next/standalone -path '*/node_modules' -prune -o -name 'server.js' -print | head -1 | xargs dirname) && \
|
||||
mkdir -p standalone && \
|
||||
cp -r "$STANDALONE_ROOT"/* standalone/ && \
|
||||
cp -r "$STANDALONE_ROOT"/.next standalone/.next && \
|
||||
# Copy node_modules if separate from app dir (monorepo structure)
|
||||
if [ -d ".next/standalone/node_modules" ] && [ "$STANDALONE_ROOT" != ".next/standalone" ]; then \
|
||||
cp -r .next/standalone/node_modules standalone/node_modules; \
|
||||
fi && \
|
||||
cp -r .next/static standalone/.next/static && \
|
||||
mkdir -p standalone/public && \
|
||||
cp -r public/* standalone/public/ 2>/dev/null || true && \
|
||||
# Verify required files exist
|
||||
test -f standalone/server.js || (echo "ERROR: server.js missing!" && exit 1) && \
|
||||
test -f standalone/.next/BUILD_ID || (echo "ERROR: BUILD_ID missing!" && exit 1)
|
||||
# Create public directory if it doesn't exist
|
||||
RUN mkdir -p public
|
||||
|
||||
# =============================================================================
|
||||
# Stage: Final Image - API Only
|
||||
@@ -122,16 +104,14 @@ FROM python:3.11-slim AS api-only
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install pg0 dependencies (procps provides 'kill' command needed by pg0)
|
||||
# Note: libicu version varies by Debian version - try common versions in order
|
||||
# Install pg0 dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
procps \
|
||||
libxml2 \
|
||||
libssl3 \
|
||||
libgssapi-krb5-2 \
|
||||
libossp-uuid16 \
|
||||
&& (apt-get install -y libicu72 2>/dev/null || apt-get install -y libicu74 2>/dev/null || apt-get install -y libicu76 2>/dev/null || true) \
|
||||
&& apt-get install -y libicu72 || apt-get install -y libicu74 || apt-get install -y libicu* \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& pip install --no-cache-dir uv
|
||||
|
||||
@@ -191,9 +171,9 @@ COPY --from=sdk-builder /app/hindsight-clients/typescript /app/sdk
|
||||
|
||||
# Copy Control Plane standalone build
|
||||
WORKDIR /app/control-plane
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/standalone ./
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/.next/static ./.next/static
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/public ./public
|
||||
COPY --from=cp-builder /app/.next/standalone ./
|
||||
COPY --from=cp-builder /app/.next/static ./.next/static
|
||||
COPY --from=cp-builder /app/public ./public
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -220,16 +200,14 @@ FROM python:3.11-slim AS standalone
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install Node.js, curl, uv, and pg0 dependencies (procps provides 'kill' command needed by pg0)
|
||||
# Note: libicu version varies by Debian version - try common versions in order
|
||||
# Install Node.js, curl, uv, and pg0 dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
procps \
|
||||
libxml2 \
|
||||
libssl3 \
|
||||
libgssapi-krb5-2 \
|
||||
libossp-uuid16 \
|
||||
&& (apt-get install -y libicu72 2>/dev/null || apt-get install -y libicu74 2>/dev/null || apt-get install -y libicu76 2>/dev/null || true) \
|
||||
&& apt-get install -y libicu72 || apt-get install -y libicu74 || apt-get install -y libicu* \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
@@ -246,9 +224,9 @@ COPY --from=sdk-builder /app/hindsight-clients/typescript /app/sdk
|
||||
|
||||
# Copy Control Plane standalone build
|
||||
WORKDIR /app/control-plane
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/standalone ./
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/.next/static ./.next/static
|
||||
COPY --from=cp-builder /app/memory-poc/hindsight-control-plane/public ./public
|
||||
COPY --from=cp-builder /app/.next/standalone ./
|
||||
COPY --from=cp-builder /app/.next/static ./.next/static
|
||||
COPY --from=cp-builder /app/public ./public
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ PIDS=()
|
||||
# Start API if enabled
|
||||
if [ "$ENABLE_API" = "true" ]; then
|
||||
cd /app/api
|
||||
# Run API directly - Python's PYTHONUNBUFFERED=1 handles output buffering
|
||||
hindsight-api &
|
||||
hindsight-api 2>&1 | sed -u 's/^/[api] /' &
|
||||
API_PID=$!
|
||||
PIDS+=($API_PID)
|
||||
|
||||
@@ -43,7 +42,7 @@ fi
|
||||
if [ "$ENABLE_CP" = "true" ]; then
|
||||
echo "🎛️ Starting Control Plane..."
|
||||
cd /app/control-plane
|
||||
PORT=9999 node server.js &
|
||||
PORT=9999 node server.js 2>&1 | grep -v -E "^[[:space:]]*(▲|✓|-|$)" | sed -u 's/^/[control-plane] /' &
|
||||
CP_PID=$!
|
||||
PIDS+=($CP_PID)
|
||||
else
|
||||
|
||||
@@ -2,8 +2,8 @@ apiVersion: v2
|
||||
name: hindsight
|
||||
description: Hindsight helm chart
|
||||
type: application
|
||||
version: 0.1.11
|
||||
appVersion: "0.1.11"
|
||||
version: 0.1.8
|
||||
appVersion: "0.1.8"
|
||||
keywords:
|
||||
- ai
|
||||
- memory
|
||||
|
||||
@@ -31,7 +31,6 @@ ENV_LOG_LEVEL = "HINDSIGHT_API_LOG_LEVEL"
|
||||
ENV_MCP_ENABLED = "HINDSIGHT_API_MCP_ENABLED"
|
||||
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"
|
||||
|
||||
# Default values
|
||||
DEFAULT_DATABASE_URL = "pg0"
|
||||
@@ -51,26 +50,6 @@ DEFAULT_MCP_ENABLED = True
|
||||
DEFAULT_GRAPH_RETRIEVER = "bfs" # Options: "bfs", "mpfp"
|
||||
DEFAULT_MCP_LOCAL_BANK_ID = "mcp"
|
||||
|
||||
# Default MCP tool descriptions (can be customized via env vars)
|
||||
DEFAULT_MCP_RETAIN_DESCRIPTION = """Store important information to long-term memory.
|
||||
|
||||
Use this tool PROACTIVELY whenever the user shares:
|
||||
- Personal facts, preferences, or interests
|
||||
- Important events or milestones
|
||||
- User history, experiences, or background
|
||||
- Decisions, opinions, or stated preferences
|
||||
- Goals, plans, or future intentions
|
||||
- Relationships or people mentioned
|
||||
- Work context, projects, or responsibilities"""
|
||||
|
||||
DEFAULT_MCP_RECALL_DESCRIPTION = """Search memories to provide personalized, context-aware responses.
|
||||
|
||||
Use this tool PROACTIVELY to:
|
||||
- Check user's preferences before making suggestions
|
||||
- Recall user's history to provide continuity
|
||||
- Remember user's goals and context
|
||||
- Personalize responses based on past interactions"""
|
||||
|
||||
# Required embedding dimension for database schema
|
||||
EMBEDDING_DIMENSION = 384
|
||||
|
||||
@@ -163,9 +142,7 @@ class HindsightConfig:
|
||||
def configure_logging(self) -> None:
|
||||
"""Configure Python logging based on the log level."""
|
||||
logging.basicConfig(
|
||||
level=self.get_python_log_level(),
|
||||
format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
|
||||
force=True, # Override any existing configuration
|
||||
level=self.get_python_log_level(), format="%(asctime)s - %(levelname)s - %(name)s - %(message)s"
|
||||
)
|
||||
|
||||
def log_config(self) -> None:
|
||||
|
||||
@@ -112,7 +112,7 @@ class LLMProvider:
|
||||
)
|
||||
await self.call(
|
||||
messages=[{"role": "user", "content": "Say 'ok'"}],
|
||||
max_completion_tokens=100,
|
||||
max_completion_tokens=10,
|
||||
max_retries=2,
|
||||
initial_backoff=0.5,
|
||||
max_backoff=2.0,
|
||||
@@ -172,7 +172,7 @@ class LLMProvider:
|
||||
|
||||
# Check if model supports reasoning parameter (o1, o3, gpt-5 families)
|
||||
model_lower = self.model.lower()
|
||||
is_reasoning_model = any(x in model_lower for x in ["gpt-5", "o1", "o3", "deepseek"])
|
||||
is_reasoning_model = any(x in model_lower for x in ["gpt-5", "o1", "o3"])
|
||||
|
||||
# For GPT-4 and GPT-4.1 models, cap max_completion_tokens to 32000
|
||||
# For GPT-4o models, cap to 16384
|
||||
@@ -194,7 +194,7 @@ class LLMProvider:
|
||||
call_params["temperature"] = temperature
|
||||
|
||||
# Set reasoning_effort for reasoning models (OpenAI gpt-5, o1, o3)
|
||||
if is_reasoning_model:
|
||||
if is_reasoning_model and self.provider == "openai":
|
||||
call_params["reasoning_effort"] = self.reasoning_effort
|
||||
|
||||
# Provider-specific parameters
|
||||
@@ -203,6 +203,7 @@ class LLMProvider:
|
||||
extra_body = {"service_tier": "auto"}
|
||||
# Only add reasoning parameters for reasoning models
|
||||
if is_reasoning_model:
|
||||
extra_body["reasoning_effort"] = self.reasoning_effort
|
||||
extra_body["include_reasoning"] = False
|
||||
call_params["extra_body"] = extra_body
|
||||
|
||||
|
||||
@@ -107,10 +107,6 @@ async def retain_batch(
|
||||
)
|
||||
|
||||
if not extracted_facts:
|
||||
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)"
|
||||
)
|
||||
return [[] for _ in contents]
|
||||
|
||||
# Apply fact_type_override if provided
|
||||
|
||||
@@ -127,10 +127,8 @@ def main():
|
||||
port=args.port,
|
||||
log_level=args.log_level,
|
||||
mcp_enabled=config.mcp_enabled,
|
||||
graph_retriever=config.graph_retriever,
|
||||
)
|
||||
config.configure_logging()
|
||||
config.log_config()
|
||||
|
||||
# Register cleanup handlers
|
||||
atexit.register(_cleanup)
|
||||
|
||||
@@ -28,15 +28,7 @@ Environment variables:
|
||||
HINDSIGHT_API_LLM_PROVIDER: Optional. LLM provider (default: "openai").
|
||||
HINDSIGHT_API_LLM_MODEL: Optional. LLM model (default: "gpt-4o-mini").
|
||||
HINDSIGHT_API_MCP_LOCAL_BANK_ID: Optional. Memory bank ID (default: "mcp").
|
||||
HINDSIGHT_API_LOG_LEVEL: Optional. Log level (default: "warning").
|
||||
HINDSIGHT_API_MCP_INSTRUCTIONS: Optional. Additional instructions appended to both retain and recall tools.
|
||||
|
||||
Example custom instructions (these are ADDED to the default behavior):
|
||||
To also store assistant actions:
|
||||
HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, including tool calls, code written, and decisions made."
|
||||
|
||||
To also store conversation summaries:
|
||||
HINDSIGHT_API_MCP_INSTRUCTIONS="Also store summaries of important conversations and their outcomes."
|
||||
HINDSIGHT_API_LOG_LEVEL: Optional. Log level (default: "info").
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -44,19 +36,14 @@ import os
|
||||
import sys
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from mcp.types import Icon
|
||||
|
||||
from hindsight_api.config import (
|
||||
DEFAULT_MCP_LOCAL_BANK_ID,
|
||||
DEFAULT_MCP_RECALL_DESCRIPTION,
|
||||
DEFAULT_MCP_RETAIN_DESCRIPTION,
|
||||
ENV_MCP_INSTRUCTIONS,
|
||||
ENV_MCP_LOCAL_BANK_ID,
|
||||
)
|
||||
|
||||
# Configure logging - default to warning to avoid polluting stderr during MCP init
|
||||
# MCP clients interpret stderr output as errors, so we suppress INFO logs by default
|
||||
_log_level_str = os.environ.get("HINDSIGHT_API_LOG_LEVEL", "warning").lower()
|
||||
# Configure logging - default to info
|
||||
_log_level_str = os.environ.get("HINDSIGHT_API_LOG_LEVEL", "info").lower()
|
||||
_log_level_map = {
|
||||
"critical": logging.CRITICAL,
|
||||
"error": logging.ERROR,
|
||||
@@ -92,21 +79,22 @@ def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
|
||||
if memory is None:
|
||||
memory = MemoryEngine(db_url="pg0://hindsight-mcp")
|
||||
|
||||
# Get custom instructions from environment variable (appended to both tools)
|
||||
extra_instructions = os.environ.get(ENV_MCP_INSTRUCTIONS, "")
|
||||
|
||||
retain_description = DEFAULT_MCP_RETAIN_DESCRIPTION
|
||||
recall_description = DEFAULT_MCP_RECALL_DESCRIPTION
|
||||
|
||||
if extra_instructions:
|
||||
retain_description = f"{DEFAULT_MCP_RETAIN_DESCRIPTION}\n\nAdditional instructions: {extra_instructions}"
|
||||
recall_description = f"{DEFAULT_MCP_RECALL_DESCRIPTION}\n\nAdditional instructions: {extra_instructions}"
|
||||
|
||||
mcp = FastMCP("hindsight")
|
||||
|
||||
@mcp.tool(description=retain_description)
|
||||
@mcp.tool()
|
||||
async def retain(content: str, context: str = "general") -> dict:
|
||||
"""
|
||||
Store important information to long-term memory.
|
||||
|
||||
Use this tool PROACTIVELY whenever the user shares:
|
||||
- Personal facts, preferences, or interests
|
||||
- Important events or milestones
|
||||
- User history, experiences, or background
|
||||
- Decisions, opinions, or stated preferences
|
||||
- Goals, plans, or future intentions
|
||||
- Relationships or people mentioned
|
||||
- Work context, projects, or responsibilities
|
||||
|
||||
Args:
|
||||
content: The fact/memory to store (be specific and include relevant details)
|
||||
context: Category for the memory (e.g., 'preferences', 'work', 'hobbies', 'family'). Default: 'general'
|
||||
@@ -123,9 +111,17 @@ def create_local_mcp_server(bank_id: str, memory=None) -> FastMCP:
|
||||
asyncio.create_task(_retain())
|
||||
return {"status": "accepted", "message": "Memory storage initiated"}
|
||||
|
||||
@mcp.tool(description=recall_description)
|
||||
@mcp.tool()
|
||||
async def recall(query: str, max_tokens: int = 4096, budget: str = "low") -> dict:
|
||||
"""
|
||||
Search memories to provide personalized, context-aware responses.
|
||||
|
||||
Use this tool PROACTIVELY to:
|
||||
- Check user's preferences before making suggestions
|
||||
- Recall user's history to provide continuity
|
||||
- Remember user's goals and context
|
||||
- Personalize responses based on past interactions
|
||||
|
||||
Args:
|
||||
query: Natural language search query (e.g., "user's food preferences", "what projects is user working on")
|
||||
max_tokens: Maximum tokens to return in results (default: 4096)
|
||||
@@ -157,9 +153,10 @@ async def _initialize_and_run(bank_id: str):
|
||||
from hindsight_api import MemoryEngine
|
||||
|
||||
# Create and initialize memory engine with pg0 embedded database
|
||||
# Note: We avoid printing to stderr during init as MCP clients show it as "errors"
|
||||
print("Initializing memory engine...", file=sys.stderr)
|
||||
memory = MemoryEngine(db_url="pg0://hindsight-mcp")
|
||||
await memory.initialize()
|
||||
print("Memory engine initialized.", file=sys.stderr)
|
||||
|
||||
# Create and run the server
|
||||
mcp = create_local_mcp_server(bank_id, memory=memory)
|
||||
@@ -182,8 +179,8 @@ def main():
|
||||
# Get bank ID from environment, default to "mcp"
|
||||
bank_id = os.environ.get(ENV_MCP_LOCAL_BANK_ID, DEFAULT_MCP_LOCAL_BANK_ID)
|
||||
|
||||
# Note: We don't print to stderr as MCP clients display it as "error output"
|
||||
# Use HINDSIGHT_API_LOG_LEVEL=debug for verbose startup logging
|
||||
# Print startup message to stderr (stdout is reserved for MCP protocol)
|
||||
print(f"Hindsight MCP server starting (bank_id={bank_id})...", file=sys.stderr)
|
||||
|
||||
# Run the async initialization and server
|
||||
asyncio.run(_initialize_and_run(bank_id))
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "hindsight-api"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
description = "Hindsight: Agent Memory That Works Like Human Memory"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "hindsight-cli"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
edition = "2021"
|
||||
authors = ["Hindsight Team"]
|
||||
description = "A beautiful CLI for Hindsight - semantic memory system"
|
||||
|
||||
@@ -91,9 +91,6 @@ enum Commands {
|
||||
#[command(alias = "tui")]
|
||||
Explore,
|
||||
|
||||
/// Launch the web-based control plane UI
|
||||
Ui,
|
||||
|
||||
/// Configure the CLI (API URL, etc.)
|
||||
#[command(after_help = "Configuration priority:\n 1. Environment variable (HINDSIGHT_API_URL) - highest priority\n 2. Config file (~/.hindsight/config)\n 3. Default (http://localhost:8888)")]
|
||||
Configure {
|
||||
@@ -376,11 +373,6 @@ fn run() -> Result<()> {
|
||||
return handle_configure(api_url, output_format);
|
||||
}
|
||||
|
||||
// Handle ui command - needs config but not API client
|
||||
if let Commands::Ui = cli.command {
|
||||
return handle_ui(output_format);
|
||||
}
|
||||
|
||||
// Load configuration
|
||||
let config = Config::from_env().unwrap_or_else(|e| {
|
||||
ui::print_error(&format!("Configuration error: {}", e));
|
||||
@@ -398,7 +390,6 @@ fn run() -> Result<()> {
|
||||
// Execute command and handle errors
|
||||
let result: Result<()> = match cli.command {
|
||||
Commands::Configure { .. } => unreachable!(), // Handled above
|
||||
Commands::Ui => unreachable!(), // Handled above
|
||||
Commands::Explore => commands::explore::run(&client),
|
||||
Commands::Bank(bank_cmd) => match bank_cmd {
|
||||
BankCommands::List => commands::bank::list(&client, verbose, output_format),
|
||||
@@ -530,50 +521,3 @@ fn handle_configure(api_url: Option<String>, output_format: OutputFormat) -> Res
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_ui(output_format: OutputFormat) -> Result<()> {
|
||||
use std::process::Command;
|
||||
|
||||
// Load configuration to get the API URL
|
||||
let config = Config::load().unwrap_or_else(|e| {
|
||||
ui::print_error(&format!("Configuration error: {}", e));
|
||||
errors::print_config_help();
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
let api_url = config.api_url();
|
||||
|
||||
if output_format == OutputFormat::Pretty {
|
||||
ui::print_info("Launching Hindsight Control Plane UI...");
|
||||
println!();
|
||||
println!(" API URL: {}", api_url);
|
||||
println!();
|
||||
}
|
||||
|
||||
// Run npx @vectorize-io/hindsight-control-plane --api-url {api_url}
|
||||
let status = Command::new("npx")
|
||||
.arg("@vectorize-io/hindsight-control-plane")
|
||||
.arg("--api-url")
|
||||
.arg(api_url)
|
||||
.status();
|
||||
|
||||
match status {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
if let Some(code) = exit_status.code() {
|
||||
std::process::exit(code);
|
||||
} else {
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ui::print_error(&format!("Failed to launch control plane UI: {}", e));
|
||||
ui::print_info("Make sure you have Node.js and npm installed.");
|
||||
ui::print_info("You can also install the control plane globally: npm install -g @vectorize-io/hindsight-control-plane");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "hindsight-client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
description = "Python client for Hindsight - Semantic memory system with personality-driven thinking"
|
||||
authors = [
|
||||
{name = "Hindsight Team"}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vectorize-io/hindsight-client",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.8",
|
||||
"description": "TypeScript client for Hindsight - Semantic memory system with personality-driven thinking",
|
||||
"main": "./dist/src/index.js",
|
||||
"types": "./dist/src/index.d.ts",
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
# production
|
||||
/build
|
||||
/standalone
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// Parse command line arguments
|
||||
let port = process.env.PORT || 9999;
|
||||
let hostname = process.env.HOSTNAME || '0.0.0.0';
|
||||
let apiUrl = process.env.HINDSIGHT_CP_DATAPLANE_API_URL;
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--port' || args[i] === '-p') {
|
||||
port = args[++i];
|
||||
} else if (args[i] === '--hostname' || args[i] === '-H') {
|
||||
hostname = args[++i];
|
||||
} else if (args[i] === '--api-url' || args[i] === '-a') {
|
||||
apiUrl = args[++i];
|
||||
} else if (args[i] === '--help' || args[i] === '-h') {
|
||||
console.log(`
|
||||
Hindsight Control Plane
|
||||
|
||||
Usage: hindsight-control-plane [options]
|
||||
|
||||
Options:
|
||||
-p, --port <port> Port to listen on (default: 9999, env: PORT)
|
||||
-H, --hostname <host> Hostname to bind to (default: 0.0.0.0, env: HOSTNAME)
|
||||
-a, --api-url <url> Hindsight API URL (env: HINDSIGHT_CP_DATAPLANE_API_URL)
|
||||
-h, --help Show this help message
|
||||
|
||||
Environment Variables:
|
||||
PORT Port to listen on
|
||||
HOSTNAME Hostname to bind to
|
||||
HINDSIGHT_CP_DATAPLANE_API_URL URL of the Hindsight API server
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the standalone server
|
||||
const standaloneDir = path.join(__dirname, '..', 'standalone');
|
||||
const serverPath = path.join(standaloneDir, 'server.js');
|
||||
|
||||
if (!fs.existsSync(serverPath)) {
|
||||
console.error('Error: Standalone server not found at', serverPath);
|
||||
console.error('This package may not have been built correctly.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Set up environment
|
||||
const env = {
|
||||
...process.env,
|
||||
PORT: String(port),
|
||||
HOSTNAME: hostname,
|
||||
};
|
||||
|
||||
if (apiUrl) {
|
||||
env.HINDSIGHT_CP_DATAPLANE_API_URL = apiUrl;
|
||||
}
|
||||
|
||||
console.log(`Starting Hindsight Control Plane on http://${hostname}:${port}`);
|
||||
if (apiUrl) {
|
||||
console.log(`API URL: ${apiUrl}`);
|
||||
}
|
||||
|
||||
// Run the standalone server
|
||||
const server = spawn('node', [serverPath], {
|
||||
cwd: standaloneDir,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
console.error('Failed to start server:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
server.on('close', (code) => {
|
||||
process.exit(code || 0);
|
||||
});
|
||||
|
||||
// Handle signals
|
||||
process.on('SIGTERM', () => server.kill('SIGTERM'));
|
||||
process.on('SIGINT', () => server.kill('SIGINT'));
|
||||
@@ -1,14 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
import path from "path";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
// Disable request logging in production
|
||||
logging: false,
|
||||
// Set the monorepo root explicitly to avoid detecting wrong lockfiles in parent directories
|
||||
turbopack: {
|
||||
root: path.resolve(__dirname, '..'),
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
{
|
||||
"name": "@vectorize-io/hindsight-control-plane",
|
||||
"version": "0.1.11",
|
||||
"description": "Control plane for Hindsight - Semantic memory system",
|
||||
"bin": {
|
||||
"hindsight-control-plane": "./bin/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"standalone",
|
||||
"public"
|
||||
],
|
||||
"name": "hindsight-control-plane",
|
||||
"version": "0.1.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build && npm run build:standalone",
|
||||
"build:standalone": "rm -rf standalone && STANDALONE_ROOT=$(find .next/standalone -path '*/node_modules' -prune -o -name 'server.js' -print | head -1 | xargs dirname) && cp -r \"$STANDALONE_ROOT\" standalone && cp -r .next/standalone/node_modules standalone/node_modules && mkdir -p standalone/.next && cp -r .next/static standalone/.next/static && mkdir -p standalone/public && cp -r public/* standalone/public/ 2>/dev/null || true",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prepublishOnly": "npm run build"
|
||||
"lint": "next lint"
|
||||
},
|
||||
"keywords": ["hindsight", "memory", "semantic", "ai"],
|
||||
"keywords": [],
|
||||
"author": "Hindsight Team",
|
||||
"license": "ISC",
|
||||
"description": "Control plane for Hindsight - Semantic memory system",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
@@ -36,6 +27,7 @@
|
||||
"@types/node": "^24.10.0",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@vectorize-io/hindsight-client": "file:../hindsight-clients/typescript",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -58,7 +50,6 @@
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vectorize-io/hindsight-client": "file:../hindsight-clients/typescript",
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { sdk, lowLevelClient } from "@/lib/hindsight-client";
|
||||
|
||||
export async function GET() {
|
||||
const status: {
|
||||
status: string;
|
||||
service: string;
|
||||
dataplane?: {
|
||||
status: string;
|
||||
url: string;
|
||||
error?: string;
|
||||
};
|
||||
} = {
|
||||
status: "ok",
|
||||
service: "hindsight-control-plane",
|
||||
};
|
||||
|
||||
// Check dataplane connectivity
|
||||
const dataplaneUrl = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888";
|
||||
try {
|
||||
await sdk.listBanks({ client: lowLevelClient });
|
||||
status.dataplane = {
|
||||
status: "connected",
|
||||
url: dataplaneUrl,
|
||||
};
|
||||
} catch (error) {
|
||||
status.dataplane = {
|
||||
status: "disconnected",
|
||||
url: dataplaneUrl,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
|
||||
return NextResponse.json(status, { status: 200 });
|
||||
}
|
||||
@@ -7,6 +7,17 @@ export async function POST(request: NextRequest) {
|
||||
const bankId = body.bank_id || body.agent_id || "default";
|
||||
const { query, types, fact_type, max_tokens, trace, budget, include, query_timestamp } = body;
|
||||
|
||||
console.log("[Recall API] Request:", {
|
||||
bankId,
|
||||
query,
|
||||
types: types || fact_type,
|
||||
max_tokens,
|
||||
trace,
|
||||
budget,
|
||||
query_timestamp,
|
||||
});
|
||||
console.log("[Recall API] Include options:", JSON.stringify(include, null, 2));
|
||||
|
||||
const response = await sdk.recallMemories({
|
||||
client: lowLevelClient,
|
||||
path: { bank_id: bankId },
|
||||
@@ -26,6 +37,18 @@ export async function POST(request: NextRequest) {
|
||||
throw new Error(`API returned no data: ${JSON.stringify(response.error || "Unknown error")}`);
|
||||
}
|
||||
|
||||
console.log("[Recall API] Response structure:", {
|
||||
hasResults: !!response.data?.results,
|
||||
resultsCount: response.data?.results?.length,
|
||||
hasTrace: !!response.data?.trace,
|
||||
hasEntities: !!response.data?.entities,
|
||||
entitiesType: typeof response.data?.entities,
|
||||
entitiesKeys: response.data?.entities ? Object.keys(response.data.entities) : null,
|
||||
hasChunks: !!response.data?.chunks,
|
||||
chunksType: typeof response.data?.chunks,
|
||||
chunksKeys: response.data?.chunks ? Object.keys(response.data.chunks) : null,
|
||||
});
|
||||
|
||||
// Return a clean JSON object by spreading the response
|
||||
// This ensures any non-serializable properties are excluded
|
||||
const jsonResponse = {
|
||||
|
||||
@@ -102,6 +102,12 @@ export function DataView({ factType }: DataViewProps) {
|
||||
bank_id: currentBank,
|
||||
type: factType,
|
||||
});
|
||||
console.log("Loaded graph data:", {
|
||||
total_units: graphData.total_units,
|
||||
nodes: graphData.nodes?.length,
|
||||
edges: graphData.edges?.length,
|
||||
table_rows: graphData.table_rows?.length,
|
||||
});
|
||||
setData(graphData);
|
||||
} catch (error) {
|
||||
console.error("Error loading data:", error);
|
||||
@@ -185,6 +191,10 @@ export function DataView({ factType }: DataViewProps) {
|
||||
otherTypes[type] = (otherTypes[type] || 0) + 1;
|
||||
}
|
||||
});
|
||||
console.log("Graph link stats:", { semantic, temporal, entity, causal, total });
|
||||
if (Object.keys(otherTypes).length > 0) {
|
||||
console.log("Other link types:", otherTypes);
|
||||
}
|
||||
return { semantic, temporal, entity, causal, total, otherTypes };
|
||||
}, [graph2DData]);
|
||||
|
||||
|
||||
@@ -214,11 +214,9 @@ def build_changelog_markdown(
|
||||
# Build markdown
|
||||
lines = [f"## [{version}]({release_url})", ""]
|
||||
|
||||
has_entries = False
|
||||
for cat_key in ["breaking", "feature", "improvement", "bugfix", "other"]:
|
||||
cat_name, cat_entries = categories[cat_key]
|
||||
if cat_entries:
|
||||
has_entries = True
|
||||
lines.append(f"**{cat_name}**")
|
||||
lines.append("")
|
||||
for entry in cat_entries:
|
||||
@@ -226,10 +224,6 @@ def build_changelog_markdown(
|
||||
lines.append(f"- {entry.summary} ([`{entry.commit_id}`]({commit_url}))")
|
||||
lines.append("")
|
||||
|
||||
if not has_entries:
|
||||
lines.append("*This release contains internal maintenance and infrastructure changes only.*")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@@ -242,8 +236,6 @@ sidebar_position: 1
|
||||
|
||||
# Changelog
|
||||
|
||||
This changelog highlights user-facing changes only. Internal maintenance, CI/CD, and infrastructure updates are omitted.
|
||||
|
||||
For full release details, see [GitHub Releases](https://github.com/vectorize-io/hindsight/releases).
|
||||
"""
|
||||
return header, ""
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "hindsight-dev"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
description = "Development utilities for Hindsight"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
@@ -4,44 +4,8 @@ sidebar_position: 1
|
||||
|
||||
# Changelog
|
||||
|
||||
This changelog highlights user-facing changes only. Internal maintenance, CI/CD, and infrastructure updates are omitted.
|
||||
|
||||
For full release details, see [GitHub Releases](https://github.com/vectorize-io/hindsight/releases).
|
||||
|
||||
## [0.1.11](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.11)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- Fixed the standalone Docker image and control plane standalone build process so standalone deployments build correctly. ([`2948cb6`](https://github.com/vectorize-io/hindsight/commit/2948cb6))
|
||||
|
||||
## [0.1.10](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.10)
|
||||
|
||||
*This release contains internal maintenance and infrastructure changes only.*
|
||||
|
||||
|
||||
## [0.1.9](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.9)
|
||||
|
||||
**Features**
|
||||
|
||||
- Simplified local MCP installation and added a standalone UI option for easier setup. ([`1c6acc3`](https://github.com/vectorize-io/hindsight/commit/1c6acc3))
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- Fixed the standalone Docker image so it builds and starts reliably. ([`b52eb90`](https://github.com/vectorize-io/hindsight/commit/b52eb90))
|
||||
- Improved Docker runtime reliability by adding required system utilities (procps). ([`ae80876`](https://github.com/vectorize-io/hindsight/commit/ae80876))
|
||||
|
||||
## [0.1.8](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.8)
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
- Fix bank list responses when a bank has no name. ([`04f01ab`](https://github.com/vectorize-io/hindsight/commit/04f01ab))
|
||||
- Fix failures when retaining memories asynchronously. ([`63f5138`](https://github.com/vectorize-io/hindsight/commit/63f5138))
|
||||
- Fix a race condition in the bank selector when switching banks. ([`e468a4e`](https://github.com/vectorize-io/hindsight/commit/e468a4e))
|
||||
|
||||
## [0.1.7](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.7)
|
||||
|
||||
*This release contains internal maintenance and infrastructure changes only.*
|
||||
|
||||
## [0.1.6](https://github.com/vectorize-io/hindsight/releases/tag/v0.1.6)
|
||||
|
||||
**Features**
|
||||
|
||||
@@ -53,7 +53,7 @@ export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
|
||||
# Ollama (local, no API key)
|
||||
export HINDSIGHT_API_LLM_PROVIDER=ollama
|
||||
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
|
||||
export HINDSIGHT_API_LLM_MODEL=gpt-oss-20b
|
||||
export HINDSIGHT_API_LLM_MODEL=llama3.1
|
||||
|
||||
# OpenAI-compatible endpoint
|
||||
export HINDSIGHT_API_LLM_PROVIDER=openai
|
||||
|
||||
@@ -133,38 +133,6 @@ hindsight-api --workers 4 # Multiple worker processes
|
||||
hindsight-api --log-level debug # Verbose logging
|
||||
```
|
||||
|
||||
### Control Plane
|
||||
|
||||
The Control Plane (Web UI) can be run standalone using npx:
|
||||
|
||||
```bash
|
||||
npx @vectorize-io/hindsight-control-plane --api-url http://localhost:8888
|
||||
```
|
||||
|
||||
This connects to your running API server and provides a visual interface for managing memory banks, exploring entities, and testing queries.
|
||||
|
||||
#### Options
|
||||
|
||||
| Option | Environment Variable | Default | Description |
|
||||
|--------|---------------------|---------|-------------|
|
||||
| `-p, --port` | `PORT` | 9999 | Port to listen on |
|
||||
| `-H, --hostname` | `HOSTNAME` | 0.0.0.0 | Hostname to bind to |
|
||||
| `-a, --api-url` | `HINDSIGHT_CP_DATAPLANE_API_URL` | http://localhost:8888 | Hindsight API URL |
|
||||
|
||||
#### Examples
|
||||
|
||||
```bash
|
||||
# Run on custom port
|
||||
npx @vectorize-io/hindsight-control-plane --port 9999 --api-url http://localhost:8888
|
||||
|
||||
# Using environment variables
|
||||
export HINDSIGHT_CP_DATAPLANE_API_URL=http://api.example.com
|
||||
npx @vectorize-io/hindsight-control-plane
|
||||
|
||||
# Production deployment
|
||||
PORT=80 HINDSIGHT_CP_DATAPLANE_API_URL=https://api.hindsight.io npx @vectorize-io/hindsight-control-plane
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -18,13 +18,7 @@ All local models (embedding, cross-encoder) are automatically downloaded from Hu
|
||||
|
||||
Used for fact extraction, entity resolution, opinion generation, and answer synthesis.
|
||||
|
||||
**Supported providers:** OpenAI, Gemini, Groq, Ollama, and **any OpenAI-compatible API**
|
||||
|
||||
:::tip OpenAI-Compatible Providers
|
||||
Hindsight works with any provider that exposes an OpenAI-compatible API (e.g., Azure OpenAI). Simply set `HINDSIGHT_API_LLM_PROVIDER=openai` and configure `HINDSIGHT_API_LLM_BASE_URL` to point to your provider's endpoint.
|
||||
|
||||
See [Configuration](./configuration#llm-provider) for setup examples.
|
||||
:::
|
||||
**Supported providers:** OpenAI, Gemini, Groq, Ollama
|
||||
|
||||
### Tested Models
|
||||
|
||||
@@ -70,7 +64,7 @@ export HINDSIGHT_API_LLM_MODEL=gemini-2.0-flash
|
||||
# Ollama (local)
|
||||
export HINDSIGHT_API_LLM_PROVIDER=ollama
|
||||
export HINDSIGHT_API_LLM_BASE_URL=http://localhost:11434/v1
|
||||
export HINDSIGHT_API_LLM_MODEL=gpt-oss-20b
|
||||
export HINDSIGHT_API_LLM_MODEL=llama3.1
|
||||
```
|
||||
|
||||
**Note:** The LLM is the primary bottleneck for retain operations. See [Performance](./performance) for optimization strategies.
|
||||
|
||||
@@ -25,9 +25,11 @@ Web UI for managing and exploring your memory banks:
|
||||
- View ingestion history and operations
|
||||
- Test recall queries interactively
|
||||
|
||||
The Control Plane connects to the API service and provides a visual interface for development and debugging.
|
||||
```
|
||||
hindsight-control-plane # Default port: 9999
|
||||
```
|
||||
|
||||
For bare metal deployments, you can run the Control Plane standalone using npx. See [Installation - Bare Metal](./installation#control-plane) for details.
|
||||
The Control Plane connects to the API service and provides a visual interface for development and debugging.
|
||||
|
||||
## Deployment Options
|
||||
|
||||
@@ -37,4 +39,4 @@ For bare metal deployments, you can run the Control Plane standalone using npx.
|
||||
| **Helm / Kubernetes** | Separate pods | Production, scaling |
|
||||
| **Bare metal** | Run independently | Custom deployments |
|
||||
|
||||
In the Docker quickstart, both services run in a single container. For production Kubernetes deployments, they run as separate pods with independent scaling. For bare metal, you can run the API via pip and the Control Plane via npx.
|
||||
In the Docker quickstart, both services run in a single container. For production Kubernetes deployments, they run as separate pods with independent scaling.
|
||||
|
||||
@@ -177,25 +177,6 @@ hindsight memory recall <bank_id> "query" -o yaml
|
||||
| `--help` | Show help |
|
||||
| `--version` | Show version |
|
||||
|
||||
## Control Plane UI
|
||||
|
||||
Launch the web-based Control Plane UI directly from the CLI:
|
||||
|
||||
```bash
|
||||
hindsight ui
|
||||
```
|
||||
|
||||
This runs the Control Plane locally on port 9999 using the API URL from your configuration. The UI provides:
|
||||
|
||||
- **Memory bank management** — Browse and manage all your banks
|
||||
- **Entity explorer** — Visualize the knowledge graph
|
||||
- **Query testing** — Interactive recall and reflect testing
|
||||
- **Operation history** — View ingestion and processing logs
|
||||
|
||||
:::tip
|
||||
The UI command requires Node.js to be installed. It automatically downloads and runs the `@vectorize-io/hindsight-control-plane` package via npx.
|
||||
:::
|
||||
|
||||
## Interactive Explorer
|
||||
|
||||
Launch the TUI explorer for visual navigation of your memory banks:
|
||||
|
||||
@@ -7,35 +7,28 @@ sidebar_position: 2
|
||||
Hindsight provides a fully local MCP server that runs entirely on your machine with an embedded PostgreSQL database. No external server or database setup required.
|
||||
|
||||
This is ideal for:
|
||||
- **Personal use with Claude Desktop** — Give Claude long-term memory across conversations
|
||||
- **Personal use with Claude Code** — Give Claude long-term memory across conversations
|
||||
- **Development and testing** — Quick setup without infrastructure
|
||||
- **Privacy-focused setups** — All data stays on your machine
|
||||
|
||||
## Quick Install
|
||||
## Quick Start
|
||||
|
||||
### With uvx (recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||||
--app claude-desktop \
|
||||
--set HINDSIGHT_API_LLM_API_KEY=sk-...
|
||||
uvx --from hindsight-api hindsight-local-mcp
|
||||
```
|
||||
|
||||
This script will:
|
||||
1. Install [uv](https://docs.astral.sh/uv/) if not already installed
|
||||
2. Configure Claude Desktop to use the Hindsight MCP server
|
||||
3. Set the provided environment variables in the MCP configuration
|
||||
### With pip
|
||||
|
||||
:::info Other MCP Applications
|
||||
The quick install script currently supports Claude Desktop only. For other MCP-compatible applications (Cursor, Cline, etc.), follow the [Manual Configuration](#manual-configuration) steps below.
|
||||
:::
|
||||
```bash
|
||||
pip install hindsight-api
|
||||
hindsight-local-mcp
|
||||
```
|
||||
|
||||
## Manual Configuration
|
||||
## Claude Code Configuration
|
||||
|
||||
Add the following to your MCP client's configuration. For Claude Desktop:
|
||||
|
||||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
||||
|
||||
For other MCP clients, refer to their documentation for the configuration file location.
|
||||
Add to your Claude Code MCP settings (`~/.claude/claude_desktop_config.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -44,7 +37,7 @@ For other MCP clients, refer to their documentation for the configuration file l
|
||||
"command": "uvx",
|
||||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
||||
"env": {
|
||||
"HINDSIGHT_API_LLM_API_KEY": "sk-..."
|
||||
"HINDSIGHT_API_LLM_API_KEY": "your-openai-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +55,7 @@ By default, memories are stored in a bank called `mcp`. To use a different bank:
|
||||
"command": "uvx",
|
||||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
||||
"env": {
|
||||
"HINDSIGHT_API_LLM_API_KEY": "sk-...",
|
||||
"HINDSIGHT_API_LLM_API_KEY": "your-openai-key",
|
||||
"HINDSIGHT_API_MCP_LOCAL_BANK_ID": "my-personal-memory"
|
||||
}
|
||||
}
|
||||
@@ -79,20 +72,6 @@ All standard [Hindsight configuration variables](/developer/configuration) are s
|
||||
| Variable | Required | Default | Description |
|
||||
|----------|----------|---------|-------------|
|
||||
| `HINDSIGHT_API_MCP_LOCAL_BANK_ID` | No | `mcp` | Memory bank ID to use |
|
||||
| `HINDSIGHT_API_MCP_INSTRUCTIONS` | No | - | Additional instructions appended to both `retain` and `recall` tools |
|
||||
|
||||
### Customizing Tool Behavior
|
||||
|
||||
You can customize what gets stored by adding instructions to the tools. Re-run the install script with the additional `--set` flag:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||||
--app claude-desktop \
|
||||
--set HINDSIGHT_API_LLM_API_KEY=sk-... \
|
||||
--set HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, code you write, and files you modify."
|
||||
```
|
||||
|
||||
These instructions are appended to the default tool descriptions, guiding Claude on when and how to use the memory tools.
|
||||
|
||||
## Available Tools
|
||||
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Install Hindsight MCP server for Claude Desktop
|
||||
#
|
||||
# Usage:
|
||||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- --app claude-desktop --set HINDSIGHT_API_LLM_API_KEY=YOUR_KEY
|
||||
#
|
||||
# Options:
|
||||
# --app Required. Target application (currently only: claude-desktop)
|
||||
# --set ENV=VALUE Set environment variable (can be repeated)
|
||||
#
|
||||
# Examples:
|
||||
# # With OpenAI
|
||||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||||
# --app claude-desktop \
|
||||
# --set HINDSIGHT_API_LLM_API_KEY=sk-...
|
||||
#
|
||||
# # With Ollama (local LLM)
|
||||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||||
# --app claude-desktop \
|
||||
# --set HINDSIGHT_API_LLM_PROVIDER=ollama \
|
||||
# --set HINDSIGHT_API_LLM_MODEL=llama3.2
|
||||
#
|
||||
# # With custom memory instructions
|
||||
# curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
||||
# --app claude-desktop \
|
||||
# --set HINDSIGHT_API_LLM_API_KEY=sk-... \
|
||||
# --set HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take and code you write."
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_banner() {
|
||||
echo ""
|
||||
# ANSI logo
|
||||
echo -e " \033[38;2;9;127;184m▄\033[0m\033[48;2;8;130;178m\033[38;2;5;133;186m▄\033[0m \033[48;2;10;143;160m\033[38;2;10;143;165m▄\033[0m\033[38;2;7;140;156m▄\033[0m "
|
||||
echo -e " \033[38;2;8;125;192m▄\033[0m \033[38;2;3;132;191m▀\033[0m\033[38;2;2;133;192m▄\033[0m \033[38;2;3;132;180m▄\033[0m\033[38;2;1;137;184m▄\033[0m\033[38;2;3;133;174m▄\033[0m \033[38;2;3;142;176m▄\033[0m\033[38;2;4;142;169m▀\033[0m \033[38;2;10;144;164m▄\033[0m "
|
||||
echo -e "\033[38;2;6;121;195m▀\033[0m\033[38;2;5;128;203m▀\033[0m\033[48;2;5;124;195m\033[38;2;3;125;200m▄\033[0m\033[38;2;2;126;196m▄\033[0m\033[48;2;3;128;188m\033[38;2;1;131;196m▄\033[0m\033[48;2;0;152;219m\033[38;2;2;131;191m▄\033[0m\033[38;2;1;141;196m▀\033[0m\033[38;2;1;135;183m▀\033[0m\033[38;2;1;148;198m▀\033[0m\033[48;2;1;156;202m\033[38;2;2;135;180m▄\033[0m\033[48;2;4;134;169m\033[38;2;1;137;177m▄\033[0m\033[38;2;3;138;173m▄\033[0m\033[48;2;6;137;165m\033[38;2;2;140;170m▄\033[0m\033[38;2;7;144;169m▀\033[0m\033[38;2;7;139;158m▀\033[0m"
|
||||
echo -e " \033[48;2;2;128;202m\033[38;2;2;124;201m▄\033[0m\033[48;2;1;130;201m\033[38;2;0;135;212m▄\033[0m\033[38;2;2;128;196m▄\033[0m \033[48;2;2;142;204m\033[38;2;7;138;199m▄\033[0m \033[38;2;1;135;186m▄\033[0m\033[48;2;1;142;186m\033[38;2;2;144;194m▄\033[0m\033[48;2;3;138;176m\033[38;2;2;134;176m▄\033[0m "
|
||||
echo -e " \033[48;2;8;118;200m\033[38;2;8;121;209m▄\033[0m\033[38;2;3;121;203m▀\033[0m \033[38;2;3;122;192m▀\033[0m\033[38;2;1;138;216m▀\033[0m\033[48;2;0;138;210m\033[38;2;3;128;198m▄\033[0m\033[48;2;0;126;188m\033[38;2;2;131;198m▄\033[0m\033[48;2;0;142;205m\033[38;2;3;132;193m▄\033[0m\033[38;2;1;140;196m▀\033[0m \033[38;2;4;134;175m▀\033[0m\033[48;2;13;135;167m\033[38;2;8;136;174m▄\033[0m "
|
||||
echo ""
|
||||
echo -e " ${BLUE}HINDSIGHT MCP INSTALLER${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
APP=""
|
||||
declare -a ENV_VARS=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--app)
|
||||
APP="$2"
|
||||
shift 2
|
||||
;;
|
||||
--set)
|
||||
ENV_VARS+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 --app <app> --set ENV=VALUE [--set ENV2=VALUE2 ...]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --app Required. Target application (currently only: claude-desktop)"
|
||||
echo " --set ENV=VALUE Set environment variable (can be repeated)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " # With OpenAI"
|
||||
echo " $0 --app claude-desktop --set HINDSIGHT_API_LLM_API_KEY=sk-..."
|
||||
echo ""
|
||||
echo " # With Ollama (local LLM, no API key needed)"
|
||||
echo " $0 --app claude-desktop --set HINDSIGHT_API_LLM_PROVIDER=ollama --set HINDSIGHT_API_LLM_MODEL=llama3.2"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Unknown option: $1. Use --help for usage."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required arguments
|
||||
if [ -z "$APP" ]; then
|
||||
print_error "Missing required argument: --app. Use --help for usage."
|
||||
fi
|
||||
|
||||
if [ "$APP" != "claude-desktop" ]; then
|
||||
print_error "Unsupported app: $APP. Currently only 'claude-desktop' is supported."
|
||||
fi
|
||||
|
||||
# Detect OS
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Darwin*) echo "macos" ;;
|
||||
Linux*) echo "linux" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) echo "windows" ;;
|
||||
*) echo "unknown" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
OS=$(detect_os)
|
||||
|
||||
# Get Claude Desktop config path based on OS
|
||||
get_claude_config_path() {
|
||||
case "$OS" in
|
||||
macos)
|
||||
echo "$HOME/Library/Application Support/Claude/claude_desktop_config.json"
|
||||
;;
|
||||
linux)
|
||||
echo "$HOME/.config/Claude/claude_desktop_config.json"
|
||||
;;
|
||||
windows)
|
||||
echo "$APPDATA/Claude/claude_desktop_config.json"
|
||||
;;
|
||||
*)
|
||||
print_error "Unsupported operating system: $OS"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check if uvx is installed and return its path
|
||||
find_uvx() {
|
||||
# Check if in PATH
|
||||
if command -v uvx &> /dev/null; then
|
||||
command -v uvx
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check common installation paths
|
||||
local paths=(
|
||||
"$HOME/.local/bin/uvx"
|
||||
"$HOME/.cargo/bin/uvx"
|
||||
"/usr/local/bin/uvx"
|
||||
)
|
||||
|
||||
for path in "${paths[@]}"; do
|
||||
if [ -f "$path" ]; then
|
||||
echo "$path"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Install uv (which includes uvx)
|
||||
install_uv() {
|
||||
print_info "Installing uv..."
|
||||
|
||||
if [ "$OS" = "windows" ]; then
|
||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
else
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
fi
|
||||
|
||||
# Source the env to get uvx in path
|
||||
if [ -f "$HOME/.local/bin/env" ]; then
|
||||
source "$HOME/.local/bin/env"
|
||||
fi
|
||||
|
||||
# Find uvx again
|
||||
if ! UVX_PATH=$(find_uvx); then
|
||||
print_error "uv installed but uvx not found. Please check your installation."
|
||||
fi
|
||||
|
||||
print_success "uv installed successfully"
|
||||
}
|
||||
|
||||
# Update Claude Desktop config
|
||||
update_claude_config() {
|
||||
local config_path="$1"
|
||||
local uvx_path="$2"
|
||||
shift 2
|
||||
local env_vars=("$@")
|
||||
|
||||
# Create config directory if it doesn't exist
|
||||
mkdir -p "$(dirname "$config_path")"
|
||||
|
||||
# Check if jq is available (needed for both new and existing configs)
|
||||
if ! command -v jq &> /dev/null; then
|
||||
print_warning "jq not found. Installing..."
|
||||
if [ "$OS" = "macos" ]; then
|
||||
if command -v brew &> /dev/null; then
|
||||
brew install jq
|
||||
else
|
||||
print_error "Please install jq: brew install jq"
|
||||
fi
|
||||
elif [ "$OS" = "linux" ]; then
|
||||
if command -v apt-get &> /dev/null; then
|
||||
sudo apt-get install -y jq
|
||||
elif command -v yum &> /dev/null; then
|
||||
sudo yum install -y jq
|
||||
else
|
||||
print_error "Please install jq manually"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build the env object from env_vars array
|
||||
local env_json="{}"
|
||||
for env_var in "${env_vars[@]}"; do
|
||||
local key="${env_var%%=*}"
|
||||
local value="${env_var#*=}"
|
||||
env_json=$(echo "$env_json" | jq --arg k "$key" --arg v "$value" '. + {($k): $v}')
|
||||
done
|
||||
|
||||
# Build the hindsight server config
|
||||
local hindsight_config
|
||||
hindsight_config=$(jq -n \
|
||||
--arg uvx "$uvx_path" \
|
||||
--argjson env "$env_json" \
|
||||
'{
|
||||
"command": $uvx,
|
||||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
||||
"env": $env
|
||||
}')
|
||||
|
||||
# Check if config file exists and has content
|
||||
if [ -f "$config_path" ] && [ -s "$config_path" ]; then
|
||||
print_info "Updating existing Claude Desktop config..."
|
||||
|
||||
# Backup existing config
|
||||
cp "$config_path" "${config_path}.backup"
|
||||
print_info "Backed up existing config to ${config_path}.backup"
|
||||
|
||||
# Add or update hindsight server in existing config
|
||||
local new_config
|
||||
new_config=$(jq --argjson hs "$hindsight_config" '.mcpServers.hindsight = $hs' "$config_path")
|
||||
|
||||
echo "$new_config" > "$config_path"
|
||||
else
|
||||
print_info "Creating new Claude Desktop config..."
|
||||
|
||||
# Create new config with hindsight server
|
||||
local new_config
|
||||
new_config=$(jq -n --argjson hs "$hindsight_config" '{"mcpServers": {"hindsight": $hs}}')
|
||||
echo "$new_config" > "$config_path"
|
||||
fi
|
||||
|
||||
print_success "Claude Desktop config updated: $config_path"
|
||||
}
|
||||
|
||||
# Main installation flow
|
||||
main() {
|
||||
print_banner
|
||||
|
||||
print_info "App: $APP"
|
||||
if [ ${#ENV_VARS[@]} -gt 0 ]; then
|
||||
print_info "Environment variables: ${#ENV_VARS[@]} configured"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 1: Check/Install uvx
|
||||
print_info "Checking for uvx..."
|
||||
if UVX_PATH=$(find_uvx); then
|
||||
print_success "uvx found at: $UVX_PATH"
|
||||
else
|
||||
print_warning "uvx not found. Installing uv..."
|
||||
install_uv
|
||||
UVX_PATH=$(find_uvx)
|
||||
fi
|
||||
|
||||
# Step 2: Update Claude Desktop config
|
||||
CONFIG_PATH=$(get_claude_config_path)
|
||||
print_info "Configuring Claude Desktop..."
|
||||
update_claude_config "$CONFIG_PATH" "$UVX_PATH" "${ENV_VARS[@]}"
|
||||
|
||||
# Done!
|
||||
echo ""
|
||||
print_success "Installation complete!"
|
||||
echo ""
|
||||
print_info "Next steps:"
|
||||
echo " 1. Restart Claude Desktop"
|
||||
echo " 2. Look for the 'hindsight' tools (retain, recall) in Claude"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "hindsight-litellm"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
description = "Universal LLM memory integration via LiteLLM - works with 100+ providers"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "hindsight-all"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
description = "Hindsight: Agent Memory That Works Like Human Memory - All-in-One Bundle"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
Generated
+5429
-117
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
ROOT_DIR="$(git rev-parse --show-toplevel)"
|
||||
cd "$ROOT_DIR" || exit 1
|
||||
cd "$ROOT_DIR/hindsight-control-plane" || exit 1
|
||||
|
||||
# Check if .env exists in workspace root
|
||||
if [ ! -f "$ROOT_DIR/.env" ]; then
|
||||
@@ -30,4 +30,4 @@ fi
|
||||
export HOSTNAME="${HINDSIGHT_CP_HOSTNAME:-0.0.0.0}"
|
||||
|
||||
# Run dev server
|
||||
npm run dev -w @vectorize-io/hindsight-control-plane
|
||||
npm run dev -w hindsight-control-plane
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Docker Smoke Test Script
|
||||
#
|
||||
# Tests that a Hindsight Docker image starts correctly and becomes healthy.
|
||||
# Can be run locally or in CI pipelines.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/docker-smoke-test.sh <image> [target]
|
||||
#
|
||||
# Arguments:
|
||||
# image - Docker image to test (e.g., hindsight-api:test, ghcr.io/vectorize-io/hindsight:latest)
|
||||
# target - Optional: 'cp-only' for control plane, otherwise assumes API image (default: api)
|
||||
#
|
||||
# Environment variables:
|
||||
# GROQ_API_KEY - Required for API/standalone images (LLM verification)
|
||||
# HINDSIGHT_API_LLM_PROVIDER - LLM provider (default: groq)
|
||||
# HINDSIGHT_API_LLM_MODEL - LLM model (default: llama-3.3-70b-versatile)
|
||||
# SMOKE_TEST_TIMEOUT - Timeout in seconds (default: 120)
|
||||
# SMOKE_TEST_CONTAINER_NAME - Container name (default: hindsight-smoke-test)
|
||||
#
|
||||
# Examples:
|
||||
# # Test a locally built image
|
||||
# ./scripts/docker-smoke-test.sh hindsight-api:test
|
||||
#
|
||||
# # Test a released image
|
||||
# ./scripts/docker-smoke-test.sh ghcr.io/vectorize-io/hindsight:latest
|
||||
#
|
||||
# # Test control plane image
|
||||
# ./scripts/docker-smoke-test.sh hindsight-control-plane:test cp-only
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 - Success (container healthy)
|
||||
# 1 - Failure (container not healthy within timeout)
|
||||
# 2 - Invalid arguments
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
IMAGE="${1:-}"
|
||||
TARGET="${2:-api}"
|
||||
TIMEOUT="${SMOKE_TEST_TIMEOUT:-120}"
|
||||
CONTAINER_NAME="${SMOKE_TEST_CONTAINER_NAME:-hindsight-smoke-test}"
|
||||
LLM_PROVIDER="${HINDSIGHT_API_LLM_PROVIDER:-groq}"
|
||||
LLM_MODEL="${HINDSIGHT_API_LLM_MODEL:-llama-3.3-70b-versatile}"
|
||||
|
||||
# Validate arguments
|
||||
if [ -z "$IMAGE" ]; then
|
||||
echo -e "${RED}Error: Image argument is required${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 <image> [target]"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 hindsight-api:test"
|
||||
echo " $0 ghcr.io/vectorize-io/hindsight:latest"
|
||||
echo " $0 hindsight-control-plane:test cp-only"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Determine health endpoint based on target
|
||||
if [ "$TARGET" = "cp-only" ]; then
|
||||
HEALTH_PORT=9999
|
||||
HEALTH_PATH="/api/health"
|
||||
NEEDS_LLM=false
|
||||
else
|
||||
HEALTH_PORT=8888
|
||||
HEALTH_PATH="/health"
|
||||
NEEDS_LLM=true
|
||||
fi
|
||||
|
||||
# Check for required environment variables
|
||||
if [ "$NEEDS_LLM" = true ] && [ -z "${GROQ_API_KEY:-}" ]; then
|
||||
echo -e "${RED}Error: GROQ_API_KEY environment variable is required for API/standalone images${NC}"
|
||||
echo "Set it with: export GROQ_API_KEY=your-api-key"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||||
docker rm "$CONTAINER_NAME" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Set trap to cleanup on exit
|
||||
trap cleanup EXIT
|
||||
|
||||
echo -e "${YELLOW}Starting smoke test for: ${IMAGE}${NC}"
|
||||
echo " Target: $TARGET"
|
||||
echo " Health endpoint: http://localhost:${HEALTH_PORT}${HEALTH_PATH}"
|
||||
echo " Timeout: ${TIMEOUT}s"
|
||||
echo ""
|
||||
|
||||
# Remove any existing container with the same name
|
||||
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
|
||||
|
||||
# Start container based on target type
|
||||
echo "Starting container..."
|
||||
if [ "$TARGET" = "cp-only" ]; then
|
||||
docker run -d --name "$CONTAINER_NAME" \
|
||||
-p "${HEALTH_PORT}:${HEALTH_PORT}" \
|
||||
"$IMAGE"
|
||||
else
|
||||
docker run -d --name "$CONTAINER_NAME" \
|
||||
-e HINDSIGHT_API_LLM_PROVIDER="$LLM_PROVIDER" \
|
||||
-e HINDSIGHT_API_LLM_API_KEY="${GROQ_API_KEY}" \
|
||||
-e HINDSIGHT_API_LLM_MODEL="$LLM_MODEL" \
|
||||
-p "${HEALTH_PORT}:${HEALTH_PORT}" \
|
||||
"$IMAGE"
|
||||
fi
|
||||
|
||||
# Wait for health endpoint
|
||||
echo "Waiting for health endpoint at http://localhost:${HEALTH_PORT}${HEALTH_PATH}..."
|
||||
start_time=$(date +%s)
|
||||
|
||||
for i in $(seq 1 "$TIMEOUT"); do
|
||||
if curl -sf "http://localhost:${HEALTH_PORT}${HEALTH_PATH}" > /dev/null 2>&1; then
|
||||
end_time=$(date +%s)
|
||||
duration=$((end_time - start_time))
|
||||
echo ""
|
||||
echo -e "${GREEN}Container is healthy after ${duration}s${NC}"
|
||||
echo ""
|
||||
echo "=== Health Response ==="
|
||||
curl -s "http://localhost:${HEALTH_PORT}${HEALTH_PATH}" | python3 -m json.tool 2>/dev/null || curl -s "http://localhost:${HEALTH_PORT}${HEALTH_PATH}"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "=== Container Logs (last 50 lines) ==="
|
||||
docker logs "$CONTAINER_NAME" 2>&1 | tail -50
|
||||
echo ""
|
||||
echo -e "${GREEN}Smoke test PASSED${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Show progress every 10 seconds
|
||||
if [ $((i % 10)) -eq 0 ]; then
|
||||
echo " Still waiting... (${i}s)"
|
||||
fi
|
||||
|
||||
# Check if container is still running
|
||||
if ! docker ps -q -f "name=$CONTAINER_NAME" | grep -q .; then
|
||||
echo ""
|
||||
echo -e "${RED}Container exited unexpectedly!${NC}"
|
||||
echo ""
|
||||
echo "=== Container Logs ==="
|
||||
docker logs "$CONTAINER_NAME" 2>&1
|
||||
echo ""
|
||||
echo -e "${RED}Smoke test FAILED${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Timeout reached
|
||||
echo ""
|
||||
echo -e "${RED}Container failed to become healthy after ${TIMEOUT}s${NC}"
|
||||
echo ""
|
||||
echo "=== Container Logs ==="
|
||||
docker logs "$CONTAINER_NAME" 2>&1
|
||||
echo ""
|
||||
echo -e "${RED}Smoke test FAILED${NC}"
|
||||
exit 1
|
||||
+2
-2
@@ -65,7 +65,7 @@ fi
|
||||
print_info "Updating version in all components..."
|
||||
|
||||
# Update Python packages
|
||||
PYTHON_PACKAGES=("hindsight-api" "hindsight-dev" "hindsight" "hindsight-integrations/litellm")
|
||||
PYTHON_PACKAGES=("hindsight-api" "hindsight-dev" "hindsight-dev/benchmarks" "hindsight" "hindsight-integrations/litellm")
|
||||
for package in "${PYTHON_PACKAGES[@]}"; do
|
||||
PYPROJECT_FILE="$package/pyproject.toml"
|
||||
if [ -f "$PYPROJECT_FILE" ]; then
|
||||
@@ -148,7 +148,7 @@ git add -A
|
||||
git commit -m "Release v$VERSION
|
||||
|
||||
- Update version to $VERSION in all components
|
||||
- Python packages: hindsight-api, hindsight-dev, hindsight-all, hindsight-litellm
|
||||
- Python packages: hindsight-api, hindsight-dev, hindsight-dev/benchmarks, hindsight-all, hindsight-litellm
|
||||
- Python client: hindsight-clients/python
|
||||
- TypeScript client: hindsight-clients/typescript
|
||||
- Rust CLI: hindsight-cli
|
||||
|
||||
@@ -1141,7 +1141,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "hindsight-all"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
source = { editable = "hindsight" }
|
||||
dependencies = [
|
||||
{ name = "hindsight-api" },
|
||||
@@ -1165,7 +1165,7 @@ provides-extras = ["test"]
|
||||
|
||||
[[package]]
|
||||
name = "hindsight-api"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
source = { editable = "hindsight-api" }
|
||||
dependencies = [
|
||||
{ name = "alembic" },
|
||||
@@ -1269,7 +1269,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "hindsight-client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
source = { editable = "hindsight-clients/python" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -1303,7 +1303,7 @@ provides-extras = ["test"]
|
||||
|
||||
[[package]]
|
||||
name = "hindsight-dev"
|
||||
version = "0.1.11"
|
||||
version = "0.1.8"
|
||||
source = { editable = "hindsight-dev" }
|
||||
dependencies = [
|
||||
{ name = "hindsight-api" },
|
||||
|
||||
Reference in New Issue
Block a user