chore: remove dead code and stale config (#2135)
Remove unreferenced backend helpers, stale UI/docs components, and unused imports across the API, control plane, clients, and integrations. Drop obsolete consolidated-observation helpers and unused scoring code, clean orphaned React/docs components, and remove stale Radix dependencies. Align release scripts, Helm docs, lockfiles, generated clients, and current API examples with the package and endpoint surface still in use.
This commit is contained in:
@@ -77,7 +77,6 @@
|
||||
"npm:@radix-ui/react-dropdown-menu@^2.1.16",
|
||||
"npm:@radix-ui/react-label@^2.1.8",
|
||||
"npm:@radix-ui/react-popover@^1.1.15",
|
||||
"npm:@radix-ui/react-radio-group@^1.3.8",
|
||||
"npm:@radix-ui/react-select@^2.2.6",
|
||||
"npm:@radix-ui/react-slider@^1.3.6",
|
||||
"npm:@radix-ui/react-slot@^1.2.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: hindsight
|
||||
# Docker Compose file for Hindsight with PostgreSQL and vectorchord
|
||||
# docker compose -f docker/docker-compose/docker-compose.yaml down && sleep 2 && docker compose -f docker/docker-compose/docker-compose.yaml up -d
|
||||
# docker compose -f docker/docker-compose/vchord/docker-compose.yaml down && sleep 2 && docker compose -f docker/docker-compose/vchord/docker-compose.yaml up -d
|
||||
# Make sure to set the required environment variables before running:
|
||||
# - HINDSIGHT_DB_PASSWORD: Password for the PostgreSQL user
|
||||
# - Configure LLM provider variables as needed (see below in the hindsight service)
|
||||
|
||||
@@ -66,13 +66,13 @@ helm install hindsight ./helm/hindsight -n hindsight --create-namespace -f value
|
||||
|
||||
| Parameter | Description | Default |
|
||||
|-----------|-------------|---------|
|
||||
| `version` | Default image tag for all components | `0.1.0` |
|
||||
| `version` | Default image tag for all components | Chart `appVersion` |
|
||||
| `api.enabled` | Enable the API component | `true` |
|
||||
| `api.image.repository` | API image repository | `hindsight/api` |
|
||||
| `api.image.repository` | API image repository | `ghcr.io/vectorize-io/hindsight-api` |
|
||||
| `api.image.tag` | API image tag (defaults to `version`) | - |
|
||||
| `api.service.port` | API service port | `8888` |
|
||||
| `controlPlane.enabled` | Enable the control plane | `true` |
|
||||
| `controlPlane.image.repository` | Control plane image repository | `hindsight/control-plane` |
|
||||
| `controlPlane.image.repository` | Control plane image repository | `ghcr.io/vectorize-io/hindsight-control-plane` |
|
||||
| `controlPlane.image.tag` | Control plane image tag (defaults to `version`) | - |
|
||||
| `controlPlane.service.port` | Control plane service port | `3000` |
|
||||
| `postgresql.enabled` | Deploy PostgreSQL as subchart | `true` |
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
# - Any other env vars you want to inject
|
||||
# existingSecret: "my-hindsight-secret"
|
||||
|
||||
# Global settings
|
||||
replicaCount: 1
|
||||
|
||||
# Image settings for api
|
||||
api:
|
||||
enabled: true
|
||||
|
||||
-2
@@ -16,9 +16,7 @@ retention parameters, retrieval settings, etc.) in Python field name format.
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import context, op
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
|
||||
from hindsight_api.alembic._dialect import run_for_dialect
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import re
|
||||
import uuid
|
||||
from collections.abc import Awaitable
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime
|
||||
from typing import Any, Literal, TypeVar
|
||||
|
||||
from fastapi import Depends, FastAPI, File, Form, Header, HTTPException, Query, Request, UploadFile
|
||||
@@ -83,7 +83,7 @@ def FieldWithDefault(default_factory: Callable, **kwargs) -> Any:
|
||||
|
||||
|
||||
from hindsight_api.config import get_config
|
||||
from hindsight_api.engine.memory_engine import Budget, _current_schema, _get_tiktoken_encoding, fq_table
|
||||
from hindsight_api.engine.memory_engine import Budget, _current_schema, _get_tiktoken_encoding
|
||||
from hindsight_api.engine.providers.none_llm import LLMNotAvailableError
|
||||
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES, MemoryFact, TokenUsage
|
||||
from hindsight_api.engine.search.tags import TagGroup, TagsMatch
|
||||
@@ -265,7 +265,7 @@ class RecallResult(BaseModel):
|
||||
|
||||
id: str
|
||||
text: str
|
||||
type: str | None = None # fact type: world, experience, opinion, observation
|
||||
type: str | None = None # fact type: world, experience, observation
|
||||
entities: list[str] | None = None # Entity names mentioned in this fact
|
||||
context: str | None = None
|
||||
occurred_start: str | None = None # ISO format date when the event started
|
||||
@@ -852,7 +852,7 @@ class ReflectFact(BaseModel):
|
||||
text: str = Field(
|
||||
description="Fact text. When type='observation', this contains markdown-formatted consolidated knowledge"
|
||||
)
|
||||
type: str | None = None # fact type: world, experience, opinion, observation
|
||||
type: str | None = None # fact type: world, experience, observation
|
||||
context: str | None = None
|
||||
occurred_start: str | None = None
|
||||
occurred_end: str | None = None
|
||||
@@ -2737,7 +2737,6 @@ def _make_audited_http(audit_logger_getter: Callable[[], AuditLogger | None]):
|
||||
from datetime import datetime as _dt
|
||||
from datetime import timezone as _tz
|
||||
from functools import wraps
|
||||
from typing import Callable as _Callable
|
||||
|
||||
def audited(action: str, *, request_param: str | None = "request"):
|
||||
"""Decorator that wraps an HTTP handler with audit logging.
|
||||
@@ -3081,8 +3080,6 @@ def create_app(
|
||||
# Replace UUIDs and numeric IDs with placeholders
|
||||
import re
|
||||
|
||||
from starlette.requests import Request
|
||||
|
||||
path = request.url.path
|
||||
# Replace UUIDs
|
||||
path = re.sub(r"/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", "/{id}", path)
|
||||
@@ -3283,7 +3280,7 @@ def _register_routes(app: FastAPI):
|
||||
"/v1/default/banks/{bank_id}/graph",
|
||||
response_model=GraphDataResponse,
|
||||
summary="Get memory graph data",
|
||||
description="Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).",
|
||||
description="Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).",
|
||||
operation_id="get_graph",
|
||||
tags=["Memory"],
|
||||
)
|
||||
@@ -3350,7 +3347,7 @@ def _register_routes(app: FastAPI):
|
||||
|
||||
Args:
|
||||
bank_id: Memory Bank ID (from path)
|
||||
type: Filter by fact type (world, experience, opinion)
|
||||
type: Filter by fact type (world, experience, observation)
|
||||
q: Search query for full-text search (searches text and context)
|
||||
consolidation_state: Filter by consolidation state for source memories
|
||||
(world/experience). One of 'failed', 'pending', or 'done'.
|
||||
@@ -3700,11 +3697,11 @@ def _register_routes(app: FastAPI):
|
||||
"/v1/default/banks/{bank_id}/reflect",
|
||||
response_model=ReflectResponse,
|
||||
summary="Reflect and generate answer",
|
||||
description="Reflect and formulate an answer using bank identity, world facts, and opinions.\n\n"
|
||||
description="Reflect and formulate an answer using bank identity, world facts, observations, and mental models.\n\n"
|
||||
"This endpoint:\n"
|
||||
"1. Retrieves experience (conversations and events)\n"
|
||||
"2. Retrieves world facts relevant to the query\n"
|
||||
"3. Retrieves existing opinions (bank's perspectives)\n"
|
||||
"3. Retrieves observations and mental models (bank's synthesized perspectives)\n"
|
||||
"4. Uses LLM to formulate a contextual answer\n"
|
||||
"5. Returns plain text answer and the facts used",
|
||||
operation_id="reflect",
|
||||
@@ -6585,7 +6582,6 @@ def _register_routes(app: FastAPI):
|
||||
_validate_parsers(_resolve_parser(request_data.parser), "request-level parser")
|
||||
|
||||
# Prepare file items and calculate total batch size
|
||||
import io
|
||||
|
||||
file_items = []
|
||||
total_batch_size = 0
|
||||
@@ -6664,14 +6660,14 @@ def _register_routes(app: FastAPI):
|
||||
"/v1/default/banks/{bank_id}/memories",
|
||||
response_model=DeleteResponse,
|
||||
summary="Clear memory bank memories",
|
||||
description="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.",
|
||||
description="Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.",
|
||||
operation_id="clear_bank_memories",
|
||||
tags=["Memory"],
|
||||
)
|
||||
@audited("clear_memories", request_param=None)
|
||||
async def api_clear_bank_memories(
|
||||
bank_id: str,
|
||||
type: str | None = Query(None, description="Optional fact type filter (world, experience, opinion)"),
|
||||
type: str | None = Query(None, description="Optional fact type filter (world, experience, observation)"),
|
||||
request_context: RequestContext = Depends(get_request_context),
|
||||
):
|
||||
"""Clear memories for a memory bank, optionally filtered by type."""
|
||||
|
||||
@@ -27,34 +27,21 @@ from ..config import (
|
||||
DEFAULT_RERANKER_LITELLM_MODEL,
|
||||
DEFAULT_RERANKER_LITELLM_SDK_MODEL,
|
||||
DEFAULT_RERANKER_LOCAL_BATCH_SIZE,
|
||||
DEFAULT_RERANKER_LOCAL_FORCE_CPU,
|
||||
DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT,
|
||||
DEFAULT_RERANKER_LOCAL_MODEL,
|
||||
DEFAULT_RERANKER_LOCAL_TRUST_REMOTE_CODE,
|
||||
DEFAULT_RERANKER_PROVIDER,
|
||||
DEFAULT_RERANKER_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_RERANKER_SILICONFLOW_MODEL,
|
||||
DEFAULT_RERANKER_TEI_BATCH_SIZE,
|
||||
DEFAULT_RERANKER_TEI_HTTP_TIMEOUT,
|
||||
DEFAULT_RERANKER_TEI_MAX_CONCURRENT,
|
||||
DEFAULT_RERANKER_ZEROENTROPY_MODEL,
|
||||
DEFAULT_ZEROENTROPY_BASE_URL,
|
||||
ENV_RERANKER_ALIBABA_API_KEY,
|
||||
ENV_RERANKER_COHERE_API_KEY,
|
||||
ENV_RERANKER_COHERE_MODEL,
|
||||
ENV_RERANKER_FLASHRANK_CACHE_DIR,
|
||||
ENV_RERANKER_FLASHRANK_CPU_MEM_ARENA,
|
||||
ENV_RERANKER_FLASHRANK_MODEL,
|
||||
ENV_RERANKER_GOOGLE_PROJECT_ID,
|
||||
ENV_RERANKER_LOCAL_FORCE_CPU,
|
||||
ENV_RERANKER_LOCAL_MAX_CONCURRENT,
|
||||
ENV_RERANKER_LOCAL_MODEL,
|
||||
ENV_RERANKER_LOCAL_TRUST_REMOTE_CODE,
|
||||
ENV_RERANKER_PROVIDER,
|
||||
ENV_RERANKER_SILICONFLOW_API_KEY,
|
||||
ENV_RERANKER_TEI_BATCH_SIZE,
|
||||
ENV_RERANKER_TEI_HTTP_TIMEOUT,
|
||||
ENV_RERANKER_TEI_MAX_CONCURRENT,
|
||||
ENV_RERANKER_TEI_URL,
|
||||
ENV_RERANKER_ZEROENTROPY_API_KEY,
|
||||
)
|
||||
@@ -303,7 +290,6 @@ class LocalSTCrossEncoder(CrossEncoderModel):
|
||||
- bucket_batching: sort pairs by token length to reduce padding waste (36-54% speedup)
|
||||
- batch_size: explicit batch size for predict() calls (MPS optimal: 32)
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
try:
|
||||
if self.bucket_batching and len(pairs) > 1:
|
||||
|
||||
@@ -19,7 +19,6 @@ and mirrors Django's ``DatabaseOperations`` architecture.
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from .base import DatabaseConnection
|
||||
from .result import ResultRow
|
||||
|
||||
@@ -8,8 +8,6 @@ columns can't appear in GROUP BY).
|
||||
import json
|
||||
import uuid as uuid_mod
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from .base import DatabaseConnection
|
||||
from .ops import DataAccessOps, TagListingParts
|
||||
|
||||
@@ -4,11 +4,6 @@ Uses unnest(), LATERAL, DISTINCT ON, and native array operations for
|
||||
efficient batch operations.
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from .base import DatabaseConnection
|
||||
from .ops import DataAccessOps, TagListingParts
|
||||
from .result import ResultRow
|
||||
@@ -620,7 +615,6 @@ class PostgreSQLOps(DataAccessOps):
|
||||
per_entity_limit: int,
|
||||
) -> tuple[list[ResultRow], list[ResultRow], list[ResultRow]]:
|
||||
# v0.5.6 array ops: unnest, &&, COUNT(DISTINCT) on source_memory_ids.
|
||||
from ..schema import fq_table
|
||||
|
||||
entity_rows = await conn.fetch(
|
||||
f"""
|
||||
|
||||
@@ -26,11 +26,8 @@ from ..config import (
|
||||
DEFAULT_EMBEDDINGS_GEMINI_MODEL,
|
||||
DEFAULT_EMBEDDINGS_LITELLM_MODEL,
|
||||
DEFAULT_EMBEDDINGS_LITELLM_SDK_MODEL,
|
||||
DEFAULT_EMBEDDINGS_LOCAL_FORCE_CPU,
|
||||
DEFAULT_EMBEDDINGS_LOCAL_MODEL,
|
||||
DEFAULT_EMBEDDINGS_LOCAL_TRUST_REMOTE_CODE,
|
||||
DEFAULT_EMBEDDINGS_OPENAI_MODEL,
|
||||
DEFAULT_EMBEDDINGS_PROVIDER,
|
||||
DEFAULT_EMBEDDINGS_ZEROENTROPY_BATCH_SIZE,
|
||||
DEFAULT_EMBEDDINGS_ZEROENTROPY_DIMENSIONS,
|
||||
DEFAULT_EMBEDDINGS_ZEROENTROPY_ENCODING_FORMAT,
|
||||
@@ -40,13 +37,6 @@ from ..config import (
|
||||
DEFAULT_ZEROENTROPY_BASE_URL,
|
||||
ENV_EMBEDDINGS_COHERE_API_KEY,
|
||||
ENV_EMBEDDINGS_GEMINI_API_KEY,
|
||||
ENV_EMBEDDINGS_LOCAL_FORCE_CPU,
|
||||
ENV_EMBEDDINGS_LOCAL_MODEL,
|
||||
ENV_EMBEDDINGS_LOCAL_TRUST_REMOTE_CODE,
|
||||
ENV_EMBEDDINGS_ONNX_DIMENSIONS,
|
||||
ENV_EMBEDDINGS_ONNX_MODEL_ID,
|
||||
ENV_EMBEDDINGS_ONNX_MODEL_PATH,
|
||||
ENV_EMBEDDINGS_ONNX_TOKENIZER_NAME_OR_PATH,
|
||||
ENV_EMBEDDINGS_OPENAI_API_KEY,
|
||||
ENV_EMBEDDINGS_OPENAI_BASE_URL,
|
||||
ENV_EMBEDDINGS_OPENAI_MODEL,
|
||||
|
||||
@@ -834,14 +834,12 @@ class EntityResolver:
|
||||
|
||||
best_candidate = None
|
||||
best_score = 0.0
|
||||
best_name_similarity = 0.0
|
||||
|
||||
nearby_entity_set = {e["text"].lower() for e in nearby_entities if e["text"] != entity_text}
|
||||
|
||||
for row in candidates:
|
||||
candidate_id = row["id"]
|
||||
canonical_name = row["canonical_name"]
|
||||
metadata = row["metadata"]
|
||||
last_seen = row["last_seen"]
|
||||
score = 0.0
|
||||
|
||||
@@ -888,7 +886,6 @@ class EntityResolver:
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_candidate = candidate_id
|
||||
best_name_similarity = name_similarity
|
||||
|
||||
# Threshold for considering it the same entity
|
||||
threshold = 0.6
|
||||
|
||||
@@ -8,7 +8,7 @@ enabling support for multiple LLM backends (OpenAI, Anthropic, Gemini, Codex, et
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from .response_models import LLMToolCallResult, TokenUsage
|
||||
from .response_models import LLMToolCallResult
|
||||
|
||||
|
||||
class LLMInterface(ABC):
|
||||
|
||||
@@ -11,14 +11,10 @@ import time
|
||||
import uuid
|
||||
from contextlib import AsyncExitStack
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from openai import APIConnectionError, APIStatusError, AsyncOpenAI, LengthFinishReasonError
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
# Vertex AI imports (conditional - for LLMProvider to pass credentials to GeminiLLM)
|
||||
try:
|
||||
import google.auth
|
||||
from google.oauth2 import service_account
|
||||
|
||||
VERTEXAI_AVAILABLE = True
|
||||
@@ -27,16 +23,14 @@ except ImportError:
|
||||
|
||||
from ..config import (
|
||||
DEFAULT_LLM_MAX_CONCURRENT,
|
||||
DEFAULT_LLM_TIMEOUT,
|
||||
ENV_CONSOLIDATION_LLM_MAX_CONCURRENT,
|
||||
ENV_LLM_GROQ_SERVICE_TIER,
|
||||
ENV_LLM_MAX_CONCURRENT,
|
||||
ENV_LLM_TIMEOUT,
|
||||
ENV_REFLECT_LLM_MAX_CONCURRENT,
|
||||
ENV_RETAIN_LLM_MAX_CONCURRENT,
|
||||
)
|
||||
from ..metrics import get_metrics_collector
|
||||
from .response_models import TokenUsage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .response_models import LLMToolCallResult
|
||||
|
||||
# Seed applied to every Groq request for deterministic behavior.
|
||||
DEFAULT_LLM_SEED = 4242
|
||||
@@ -287,7 +281,6 @@ def create_llm_provider(
|
||||
Returns:
|
||||
LLMInterface implementation for the specified provider.
|
||||
"""
|
||||
from .llm_interface import LLMInterface
|
||||
from .providers import (
|
||||
AnthropicLLM,
|
||||
ClaudeCodeLLM,
|
||||
|
||||
@@ -36,8 +36,6 @@ from ..config import (
|
||||
HindsightConfig,
|
||||
get_config,
|
||||
)
|
||||
from ..db_url import to_libpq_url
|
||||
from ..metrics import get_metrics_collector
|
||||
from ..tracing import create_operation_span
|
||||
from ..utils import mask_network_location
|
||||
from ..worker.exceptions import DeferOperation, RetryTaskAt
|
||||
@@ -58,10 +56,7 @@ from .llm_trace import (
|
||||
from .operation_metadata import (
|
||||
BatchRetainChildMetadata,
|
||||
BatchRetainParentMetadata,
|
||||
ConsolidationMetadata,
|
||||
RefreshMentalModelMetadata,
|
||||
RetainExtractionErrors,
|
||||
RetainMetadata,
|
||||
RetainOutcomeAggregate,
|
||||
RetainOutcomeMetadata,
|
||||
)
|
||||
@@ -334,16 +329,12 @@ def validate_sql_schema(sql: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
import asyncpg
|
||||
import numpy as np
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from .cross_encoder import CrossEncoderModel
|
||||
from .embeddings import Embeddings, create_embeddings_from_env
|
||||
from .interface import MemoryEngineInterface
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from hindsight_api.extensions import OperationValidatorExtension, TenantExtension
|
||||
from hindsight_api.extensions import OperationValidatorExtension, TenantExtension, ValidationResult
|
||||
from hindsight_api.models import RequestContext
|
||||
|
||||
from .audit import AuditLogListResponse, AuditLogStatsResponse
|
||||
@@ -352,21 +343,17 @@ if TYPE_CHECKING:
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from ..metrics import get_metrics_collector
|
||||
from ..pg0 import EmbeddedPostgres, parse_pg0_url
|
||||
from .entity_resolver import EntityResolver
|
||||
from .llm_wrapper import LLMConfig, requires_api_key, sanitize_llm_output, sanitize_text
|
||||
from .query_analyzer import QueryAnalyzer
|
||||
from .reflect import run_reflect_agent
|
||||
from .reflect.prompts import DELTA_SYSTEM_PROMPT, build_delta_prompt
|
||||
from .reflect.tools import tool_expand, tool_recall, tool_search_mental_models, tool_search_observations
|
||||
from .response_models import (
|
||||
VALID_RECALL_FACT_TYPES,
|
||||
EntityObservation,
|
||||
EntityState,
|
||||
LLMCallTrace,
|
||||
MemoryFact,
|
||||
ObservationRef,
|
||||
ReflectResult,
|
||||
TokenUsage,
|
||||
ToolCallTrace,
|
||||
@@ -374,7 +361,6 @@ from .response_models import (
|
||||
from .response_models import RecallResult as RecallResultModel
|
||||
from .retain import bank_utils, embedding_utils
|
||||
from .retain.types import RetainContentDict
|
||||
from .search import think_utils
|
||||
from .search.reranking import CrossEncoderReranker, apply_combined_scoring
|
||||
from .search.tags import TagGroup, TagsMatch, build_tag_groups_where_clause, build_tags_where_clause
|
||||
from .search.types import ScoredResult
|
||||
@@ -1161,7 +1147,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
if self._operation_validator is None:
|
||||
return None
|
||||
|
||||
from hindsight_api.extensions import OperationValidationError, ValidationResult
|
||||
from hindsight_api.extensions import OperationValidationError
|
||||
|
||||
result = await validation_coro
|
||||
if not result.allowed:
|
||||
@@ -3115,7 +3101,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
)
|
||||
result = await self._validate_operation(self._operation_validator.validate_retain(ctx))
|
||||
if result and result.contents is not None:
|
||||
contents = result.contents
|
||||
contents = cast(list[RetainContentDict], result.contents)
|
||||
|
||||
# Engine-owned copy: the orchestrator clears per-item "content" strings
|
||||
# after building the document's combined text (memory pressure
|
||||
@@ -3476,7 +3462,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
# Use the new modular orchestrator
|
||||
from .retain import orchestrator
|
||||
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
|
||||
# Resolve bank-specific config for this operation
|
||||
resolved_config = await self._config_resolver.resolve_full_config(bank_id, request_context)
|
||||
@@ -3612,7 +3598,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
parse_archive(archive_bytes)
|
||||
|
||||
await self._authenticate_tenant(request_context)
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
# Ensure the bank (and its per-bank vector indexes) exist before inserts.
|
||||
# Import has no single write transaction to join — the archive is written
|
||||
# by a worker later — so the bank is created on its own connection.
|
||||
@@ -4178,9 +4164,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
retrieve_all_fact_types_parallel,
|
||||
)
|
||||
|
||||
# Track each retrieval start time
|
||||
retrieval_start = time.time()
|
||||
|
||||
retrieval_span = tracer_otel.start_span("hindsight.recall_retrieval")
|
||||
retrieval_span.set_attribute("hindsight.bank_id", bank_id)
|
||||
retrieval_span.set_attribute("hindsight.fact_types", ",".join(fact_type))
|
||||
@@ -4291,10 +4274,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
f"graph {pre_cap_counts[2]}->{len(graph_results)}"
|
||||
)
|
||||
|
||||
retrieval_duration = time.time() - retrieval_start
|
||||
|
||||
step_duration = time.time() - step_start
|
||||
total_retrievals = len(fact_type) * (4 if temporal_results else 3)
|
||||
# Format per-method timings
|
||||
timing_parts = [
|
||||
f"semantic={len(semantic_results)}({aggregated_timings['semantic']:.3f}s)",
|
||||
@@ -8081,7 +8061,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
bank_id=bank_id, operation="update_bank_disposition", request_context=request_context
|
||||
)
|
||||
await self._validate_operation(self._operation_validator.validate_bank_write(ctx))
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
await bank_utils.update_bank_disposition(self._backend, bank_id, disposition)
|
||||
|
||||
async def set_bank_mission(
|
||||
@@ -8108,7 +8088,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
|
||||
ctx = BankWriteContext(bank_id=bank_id, operation="set_bank_mission", request_context=request_context)
|
||||
await self._validate_operation(self._operation_validator.validate_bank_write(ctx))
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
await bank_utils.set_bank_mission(self._backend, bank_id, mission)
|
||||
return {"bank_id": bank_id, "mission": mission}
|
||||
|
||||
@@ -8137,7 +8117,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
|
||||
ctx = BankWriteContext(bank_id=bank_id, operation="merge_bank_mission", request_context=request_context)
|
||||
await self._validate_operation(self._operation_validator.validate_bank_write(ctx))
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
return await bank_utils.merge_bank_mission(self._backend, self._reflect_llm_config, bank_id, new_info)
|
||||
|
||||
async def list_banks(
|
||||
@@ -8155,7 +8135,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
List of dicts with bank_id, name, disposition, mission, created_at, updated_at
|
||||
"""
|
||||
await self._authenticate_tenant(request_context)
|
||||
backend = await self._get_backend()
|
||||
await self._get_backend()
|
||||
banks = await bank_utils.list_banks(self._backend)
|
||||
if self._operation_validator:
|
||||
from hindsight_api.extensions import BankListContext
|
||||
@@ -9432,87 +9412,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
"observations": [],
|
||||
}
|
||||
|
||||
def _parse_observations(self, observations_raw: list):
|
||||
"""Parse raw observation dicts into typed Observation models.
|
||||
|
||||
Returns list of Observation models with computed trend/evidence_span/evidence_count.
|
||||
"""
|
||||
from .reflect.observations import Observation, ObservationEvidence
|
||||
|
||||
observations: list[Observation] = []
|
||||
for obs in observations_raw:
|
||||
if not isinstance(obs, dict):
|
||||
continue
|
||||
|
||||
try:
|
||||
parsed = Observation(
|
||||
title=obs.get("title", ""),
|
||||
content=obs.get("content", ""),
|
||||
evidence=[
|
||||
ObservationEvidence(
|
||||
memory_id=ev.get("memory_id", ""),
|
||||
quote=ev.get("quote", ""),
|
||||
relevance=ev.get("relevance", ""),
|
||||
timestamp=ev.get("timestamp"),
|
||||
)
|
||||
for ev in obs.get("evidence", [])
|
||||
if isinstance(ev, dict)
|
||||
],
|
||||
created_at=obs.get("created_at"),
|
||||
)
|
||||
observations.append(parsed)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to parse observation: {e}")
|
||||
continue
|
||||
|
||||
return observations
|
||||
|
||||
async def _count_memories_since(
|
||||
self,
|
||||
bank_id: str,
|
||||
since_timestamp: str | None,
|
||||
backend=None,
|
||||
) -> int:
|
||||
"""
|
||||
Count memories created after a given timestamp.
|
||||
|
||||
Args:
|
||||
bank_id: Bank identifier
|
||||
since_timestamp: ISO timestamp string. If None, returns total count.
|
||||
backend: Optional database backend (uses default if not provided)
|
||||
|
||||
Returns:
|
||||
Number of memories created since the timestamp
|
||||
"""
|
||||
if backend is None:
|
||||
backend = await self._get_backend()
|
||||
|
||||
async with acquire_with_retry(backend) as conn:
|
||||
if since_timestamp:
|
||||
# Parse the timestamp
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
ts = datetime.fromisoformat(since_timestamp.replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
# Invalid timestamp, return total count
|
||||
ts = None
|
||||
|
||||
if ts:
|
||||
count = await conn.fetchval(
|
||||
f"SELECT COUNT(*) FROM {fq_table('memory_units')} WHERE bank_id = $1 AND created_at > $2",
|
||||
bank_id,
|
||||
ts,
|
||||
)
|
||||
return count or 0
|
||||
|
||||
# No timestamp or invalid, return total count
|
||||
count = await conn.fetchval(
|
||||
f"SELECT COUNT(*) FROM {fq_table('memory_units')} WHERE bank_id = $1",
|
||||
bank_id,
|
||||
)
|
||||
return count or 0
|
||||
|
||||
async def _delete_stale_observations_for_memories(
|
||||
self,
|
||||
conn,
|
||||
@@ -9529,149 +9428,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
|
||||
return await delete_stale_observations_for_memories(conn, bank_id, fact_ids, ops=self._backend.ops)
|
||||
|
||||
# =========================================================================
|
||||
# MENTAL MODELS (CONSOLIDATED) - Read-only access to auto-consolidated mental models
|
||||
# =========================================================================
|
||||
|
||||
async def list_mental_models_consolidated(
|
||||
self,
|
||||
bank_id: str,
|
||||
*,
|
||||
tags: list[str] | None = None,
|
||||
tags_match: str = "any",
|
||||
limit: int = 100,
|
||||
offset: int = 0,
|
||||
request_context: "RequestContext",
|
||||
) -> list[dict[str, Any]]:
|
||||
"""List auto-consolidated observations for a bank.
|
||||
|
||||
Observations are stored in memory_units with fact_type='observation'.
|
||||
They are automatically created and updated by the consolidation engine.
|
||||
|
||||
Args:
|
||||
bank_id: Bank identifier
|
||||
tags: Optional tags to filter by
|
||||
tags_match: How to match tags - 'any', 'all', or 'exact'
|
||||
limit: Maximum number of results
|
||||
offset: Offset for pagination
|
||||
request_context: Request context for authentication
|
||||
|
||||
Returns:
|
||||
List of observation dicts
|
||||
"""
|
||||
await self._authenticate_tenant(request_context)
|
||||
backend = await self._get_backend()
|
||||
|
||||
async with acquire_with_retry(backend) as conn:
|
||||
# Build tag filter
|
||||
tag_filter = ""
|
||||
params: list[Any] = [bank_id, limit, offset]
|
||||
if tags:
|
||||
if tags_match == "all":
|
||||
tag_filter = " AND tags @> $4::varchar[]"
|
||||
elif tags_match == "exact":
|
||||
tag_filter = " AND tags = $4::varchar[]"
|
||||
else: # any
|
||||
tag_filter = " AND tags && $4::varchar[]"
|
||||
params.append(tags)
|
||||
|
||||
rows = await conn.fetch(
|
||||
f"""
|
||||
SELECT id, bank_id, text, proof_count, tags, source_memory_ids, created_at, updated_at
|
||||
FROM {fq_table("memory_units")}
|
||||
WHERE bank_id = $1 AND fact_type = 'observation' {tag_filter}
|
||||
ORDER BY updated_at DESC NULLS LAST
|
||||
LIMIT $2 OFFSET $3
|
||||
""",
|
||||
*params,
|
||||
)
|
||||
|
||||
return [self._row_to_observation_consolidated(row) for row in rows]
|
||||
|
||||
async def get_observation_consolidated(
|
||||
self,
|
||||
bank_id: str,
|
||||
observation_id: str,
|
||||
*,
|
||||
include_source_memories: bool = True,
|
||||
request_context: "RequestContext",
|
||||
) -> dict[str, Any] | None:
|
||||
"""Get a single observation by ID.
|
||||
|
||||
Args:
|
||||
bank_id: Bank identifier
|
||||
observation_id: Observation ID
|
||||
include_source_memories: Whether to include full source memory details
|
||||
request_context: Request context for authentication
|
||||
|
||||
Returns:
|
||||
Observation dict or None if not found
|
||||
"""
|
||||
await self._authenticate_tenant(request_context)
|
||||
backend = await self._get_backend()
|
||||
|
||||
async with acquire_with_retry(backend) as conn:
|
||||
row = await conn.fetchrow(
|
||||
f"""
|
||||
SELECT id, bank_id, text, proof_count, tags, source_memory_ids, created_at, updated_at
|
||||
FROM {fq_table("memory_units")}
|
||||
WHERE bank_id = $1 AND id = $2 AND fact_type = 'observation'
|
||||
""",
|
||||
bank_id,
|
||||
observation_id,
|
||||
)
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
result = self._row_to_observation_consolidated(row)
|
||||
|
||||
# Fetch source memories if requested and source_memory_ids exist
|
||||
if include_source_memories and result.get("source_memory_ids"):
|
||||
source_ids = [uuid.UUID(sid) if isinstance(sid, str) else sid for sid in result["source_memory_ids"]]
|
||||
source_rows = await conn.fetch(
|
||||
f"""
|
||||
SELECT id, text, fact_type, context, occurred_start, mentioned_at
|
||||
FROM {fq_table("memory_units")}
|
||||
WHERE id = ANY($1::uuid[])
|
||||
ORDER BY mentioned_at DESC NULLS LAST
|
||||
""",
|
||||
source_ids,
|
||||
)
|
||||
result["source_memories"] = [
|
||||
{
|
||||
"id": str(r["id"]),
|
||||
"text": r["text"],
|
||||
"type": r["fact_type"],
|
||||
"context": r["context"],
|
||||
"occurred_start": r["occurred_start"].isoformat() if r["occurred_start"] else None,
|
||||
"mentioned_at": r["mentioned_at"].isoformat() if r["mentioned_at"] else None,
|
||||
}
|
||||
for r in source_rows
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
def _row_to_observation_consolidated(self, row: Any) -> dict[str, Any]:
|
||||
"""Convert a database row to an observation dict."""
|
||||
# Convert source_memory_ids to strings
|
||||
source_memory_ids = row.get("source_memory_ids") or []
|
||||
source_memory_ids = [str(sid) for sid in source_memory_ids]
|
||||
|
||||
return {
|
||||
"id": str(row["id"]),
|
||||
"bank_id": row["bank_id"],
|
||||
"text": row["text"],
|
||||
"proof_count": row["proof_count"] or 1,
|
||||
# Deprecated inline field — full history via GET .../{id}/history.
|
||||
"history": [],
|
||||
"tags": row["tags"] or [],
|
||||
"source_memory_ids": source_memory_ids,
|
||||
"source_memories": [], # Populated separately when fetching full details
|
||||
"created_at": row["created_at"].isoformat() if row["created_at"] else None,
|
||||
"updated_at": row["updated_at"].isoformat() if row["updated_at"] else None,
|
||||
}
|
||||
|
||||
# =========================================================================
|
||||
# MENTAL MODELS CRUD
|
||||
# =========================================================================
|
||||
@@ -11139,10 +10895,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
# Parent operations have their status updated when all children complete/fail
|
||||
operation_list = []
|
||||
for row in operations:
|
||||
# Map DB status to API status (pending includes processing)
|
||||
db_status = row["status"]
|
||||
api_status = "pending" if db_status in ("pending", "processing") else db_status
|
||||
|
||||
result_metadata = conn.parse_json(row["result_metadata"]) or {}
|
||||
|
||||
next_retry_at = row["next_retry_at"]
|
||||
@@ -11237,7 +10989,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
child_statuses = []
|
||||
all_done = True
|
||||
any_failed = False
|
||||
all_completed = True
|
||||
|
||||
for child_row in child_rows:
|
||||
raw_crm = child_row["result_metadata"]
|
||||
@@ -11258,9 +11009,6 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
all_done = False
|
||||
if child_status == "failed":
|
||||
any_failed = True
|
||||
if child_status != "completed":
|
||||
all_completed = False
|
||||
|
||||
# Self-healing: if parent status is out of sync with children, update it
|
||||
if all_done and api_status == "pending":
|
||||
correct_status = "failed" if any_failed else "completed"
|
||||
|
||||
@@ -14,7 +14,7 @@ import logging
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from hindsight_api.engine.llm_interface import LLMInterface, OutputTooLongError
|
||||
from hindsight_api.engine.llm_interface import LLMInterface
|
||||
from hindsight_api.engine.response_models import LLMToolCall, LLMToolCallResult, TokenUsage
|
||||
from hindsight_api.metrics import get_metrics_collector
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import Any
|
||||
|
||||
from pydantic import ValidationError
|
||||
|
||||
from hindsight_api.engine.llm_interface import LLMInterface, OutputTooLongError
|
||||
from hindsight_api.engine.llm_interface import LLMInterface
|
||||
from hindsight_api.engine.response_models import LLMToolCall, LLMToolCallResult, TokenUsage
|
||||
from hindsight_api.metrics import get_metrics_collector
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from hindsight_api.engine.llm_interface import LLMInterface, OutputTooLongError
|
||||
from hindsight_api.engine.llm_interface import LLMInterface
|
||||
from hindsight_api.engine.response_models import LLMToolCall, LLMToolCallResult, TokenUsage
|
||||
from hindsight_api.metrics import get_metrics_collector
|
||||
|
||||
@@ -397,7 +397,6 @@ class CodexLLM(LLMInterface):
|
||||
}
|
||||
|
||||
url = f"{self.base_url}/codex/responses"
|
||||
last_exception = None
|
||||
|
||||
# Manual attempt tracking instead of ``for attempt in range(...)`` so
|
||||
# that the reactive-refresh path can retry once without consuming a
|
||||
@@ -428,7 +427,6 @@ class CodexLLM(LLMInterface):
|
||||
if attempt < max_retries:
|
||||
backoff = min(initial_backoff * (2**attempt), max_backoff)
|
||||
await asyncio.sleep(backoff)
|
||||
last_exception = e
|
||||
attempt += 1
|
||||
continue
|
||||
raise
|
||||
@@ -490,7 +488,6 @@ class CodexLLM(LLMInterface):
|
||||
return result
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
last_exception = e
|
||||
status_code = e.response.status_code
|
||||
|
||||
# Auth error: try one OAuth refresh + retry before giving up.
|
||||
@@ -549,7 +546,6 @@ class CodexLLM(LLMInterface):
|
||||
raise
|
||||
|
||||
except httpx.RequestError as e:
|
||||
last_exception = e
|
||||
if attempt < max_retries:
|
||||
backoff = min(initial_backoff * (2**attempt), max_backoff)
|
||||
logger.warning(f"Codex connection error (attempt {attempt + 1}/{max_retries + 1}): {e}")
|
||||
@@ -564,10 +560,6 @@ class CodexLLM(LLMInterface):
|
||||
logger.error(f"Unexpected Codex error: {type(e).__name__}: {e}")
|
||||
raise
|
||||
|
||||
if last_exception:
|
||||
raise last_exception
|
||||
raise RuntimeError("Codex call failed after all retries")
|
||||
|
||||
async def _parse_sse_stream(self, response: httpx.Response) -> str:
|
||||
"""
|
||||
Parse Server-Sent Events (SSE) stream from Codex API.
|
||||
|
||||
@@ -11,7 +11,6 @@ import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from contextvars import ContextVar
|
||||
from typing import Any
|
||||
@@ -20,7 +19,7 @@ from google import genai
|
||||
from google.genai import errors as genai_errors
|
||||
from google.genai import types as genai_types
|
||||
|
||||
from hindsight_api.engine.llm_interface import LLMInterface, OutputTooLongError
|
||||
from hindsight_api.engine.llm_interface import LLMInterface
|
||||
from hindsight_api.engine.llm_wrapper import parse_llm_json
|
||||
from hindsight_api.engine.response_models import LLMToolCall, LLMToolCallResult, TokenUsage
|
||||
from hindsight_api.metrics import get_metrics_collector
|
||||
@@ -36,7 +35,6 @@ _safety_settings_ctx: ContextVar[list | None] = ContextVar("gemini_safety_settin
|
||||
|
||||
# Vertex AI imports (optional)
|
||||
try:
|
||||
import google.auth
|
||||
from google.oauth2 import service_account
|
||||
|
||||
VERTEXAI_AVAILABLE = True
|
||||
|
||||
@@ -4,7 +4,6 @@ bank profile utilities for disposition and mission management.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from typing import TypedDict
|
||||
|
||||
@@ -14,7 +14,6 @@ from typing import Any, Literal, cast
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, create_model, field_validator
|
||||
|
||||
from ...config import get_config
|
||||
from ..llm_wrapper import LLMConfig, OutputTooLongError, sanitize_llm_output
|
||||
from ..operation_metadata import RetainExtractionErrors
|
||||
from ..response_models import TokenUsage
|
||||
@@ -1881,8 +1880,7 @@ async def extract_facts_from_contents_batch_api(
|
||||
|
||||
logger.info(f"Using Batch API for fact extraction ({len(contents)} contents)")
|
||||
|
||||
# Check config for extraction mode and causal link extraction (used throughout)
|
||||
extraction_mode = config.retain_extraction_mode
|
||||
# Check config for causal link extraction (used throughout)
|
||||
extract_causal_links = config.retain_extract_causal_links
|
||||
|
||||
# Check if provider supports batch API
|
||||
|
||||
@@ -800,8 +800,6 @@ async def create_causal_links_batch(
|
||||
try:
|
||||
import time as time_mod
|
||||
|
||||
create_start = time_mod.time()
|
||||
|
||||
# Build links list
|
||||
links = []
|
||||
for fact_idx, causal_relations in enumerate(causal_relations_per_fact):
|
||||
|
||||
@@ -23,7 +23,6 @@ from ...extensions.memory_defense import (
|
||||
parse_policy,
|
||||
)
|
||||
from ...worker.stage import set_stage
|
||||
from ..db.base import DatabaseBackend
|
||||
from ..db_utils import acquire_with_retry
|
||||
from ..memory_engine import count_tokens, fq_table
|
||||
from . import bank_utils
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
Helper functions for hybrid search (semantic + BM25 + graph).
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .types import MergedCandidate, RetrievalResult
|
||||
|
||||
|
||||
@@ -156,39 +154,3 @@ def interleave_fusion(result_lists: list[list[RetrievalResult]]) -> list[MergedC
|
||||
)
|
||||
for pos, doc_id in enumerate(ordered_ids)
|
||||
]
|
||||
|
||||
|
||||
def normalize_scores_on_deltas(results: list[dict[str, Any]], score_keys: list[str]) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Normalize scores based on deltas (min-max normalization within result set).
|
||||
|
||||
This ensures all scores are in [0, 1] range based on the spread in THIS result set.
|
||||
|
||||
Args:
|
||||
results: List of result dicts
|
||||
score_keys: Keys to normalize (e.g., ["recency", "frequency"])
|
||||
|
||||
Returns:
|
||||
Results with normalized scores added as "{key}_normalized"
|
||||
"""
|
||||
for key in score_keys:
|
||||
values = [r.get(key, 0.0) for r in results if key in r]
|
||||
|
||||
if not values:
|
||||
continue
|
||||
|
||||
min_val = min(values)
|
||||
max_val = max(values)
|
||||
delta = max_val - min_val
|
||||
|
||||
if delta > 0:
|
||||
for r in results:
|
||||
if key in r:
|
||||
r[f"{key}_normalized"] = (r[key] - min_val) / delta
|
||||
else:
|
||||
# All values are the same, set to 0.5
|
||||
for r in results:
|
||||
if key in r:
|
||||
r[f"{key}_normalized"] = 0.5
|
||||
|
||||
return results
|
||||
|
||||
@@ -13,7 +13,7 @@ import logging
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any, Optional
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
from ...config import get_config
|
||||
from ..db_utils import acquire_with_retry
|
||||
@@ -24,6 +24,9 @@ from .link_expansion_retrieval import LinkExpansionRetriever
|
||||
from .tags import TagGroup, TagsMatch, build_tag_groups_where_clause, build_tags_where_clause_simple
|
||||
from .types import GraphRetrievalTimings, RetrievalResult
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..query_analyzer import QueryAnalyzer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import timedelta, timezone
|
||||
|
||||
import obstore as obs
|
||||
from obstore.store import GCSStore
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from hindsight_api.extensions.base import Extension
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import time
|
||||
import traceback
|
||||
from collections import Counter
|
||||
from collections.abc import Awaitable, Callable, Iterable
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ..engine.schema import fq_table_explicit as fq_table
|
||||
@@ -824,7 +824,6 @@ class WorkerPoller:
|
||||
recovered = 0
|
||||
for row in rows:
|
||||
operation_id = str(row["operation_id"])
|
||||
task_payload = row["task_payload"]
|
||||
result_metadata = row["result_metadata"]
|
||||
|
||||
# Parse metadata
|
||||
@@ -838,12 +837,6 @@ class WorkerPoller:
|
||||
f"Recovering batch operation: operation_id={operation_id}, batch_id={batch_id}, provider={batch_provider}"
|
||||
)
|
||||
|
||||
# Parse task_payload
|
||||
if isinstance(task_payload, str):
|
||||
task_dict = json.loads(task_payload)
|
||||
else:
|
||||
task_dict = task_payload
|
||||
|
||||
# Mark operation as ready for re-processing
|
||||
# Reset to pending with task_payload intact so worker picks it up again
|
||||
async with self._backend.acquire() as conn:
|
||||
|
||||
@@ -55,7 +55,7 @@ paths:
|
||||
/v1/default/banks/{bank_id}/graph:
|
||||
get:
|
||||
description: "Retrieve graph data for visualization, optionally filtered by\
|
||||
\ type (world/experience/opinion)."
|
||||
\ type (world/experience/observation)."
|
||||
operationId: get_graph
|
||||
parameters:
|
||||
- explode: false
|
||||
@@ -442,12 +442,12 @@ paths:
|
||||
/v1/default/banks/{bank_id}/reflect:
|
||||
post:
|
||||
description: |-
|
||||
Reflect and formulate an answer using bank identity, world facts, and opinions.
|
||||
Reflect and formulate an answer using bank identity, world facts, observations, and mental models.
|
||||
|
||||
This endpoint:
|
||||
1. Retrieves experience (conversations and events)
|
||||
2. Retrieves world facts relevant to the query
|
||||
3. Retrieves existing opinions (bank's perspectives)
|
||||
3. Retrieves observations and mental models (bank's synthesized perspectives)
|
||||
4. Uses LLM to formulate a contextual answer
|
||||
5. Returns plain text answer and the facts used
|
||||
operationId: reflect
|
||||
@@ -3331,9 +3331,9 @@ paths:
|
||||
/v1/default/banks/{bank_id}/memories:
|
||||
delete:
|
||||
description: "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."
|
||||
\ (world, experience, observation) to delete only specific types. This is\
|
||||
\ a destructive operation that cannot be undone. The bank profile (disposition\
|
||||
\ and background) will be preserved."
|
||||
operationId: clear_bank_memories
|
||||
parameters:
|
||||
- explode: false
|
||||
@@ -3344,7 +3344,7 @@ paths:
|
||||
title: Bank Id
|
||||
type: string
|
||||
style: simple
|
||||
- description: "Optional fact type filter (world, experience, opinion)"
|
||||
- description: "Optional fact type filter (world, experience, observation)"
|
||||
explode: true
|
||||
in: query
|
||||
name: type
|
||||
|
||||
@@ -32,7 +32,7 @@ type ApiClearBankMemoriesRequest struct {
|
||||
authorization *string
|
||||
}
|
||||
|
||||
// Optional fact type filter (world, experience, opinion)
|
||||
// Optional fact type filter (world, experience, observation)
|
||||
func (r ApiClearBankMemoriesRequest) Type_(type_ string) ApiClearBankMemoriesRequest {
|
||||
r.type_ = &type_
|
||||
return r
|
||||
@@ -50,7 +50,7 @@ func (r ApiClearBankMemoriesRequest) Execute() (*DeleteResponse, *http.Response,
|
||||
/*
|
||||
ClearBankMemories 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.
|
||||
Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param bankId
|
||||
@@ -343,7 +343,7 @@ func (r ApiGetGraphRequest) Execute() (*GraphDataResponse, *http.Response, error
|
||||
/*
|
||||
GetGraph Get memory graph data
|
||||
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param bankId
|
||||
@@ -1257,12 +1257,12 @@ func (r ApiReflectRequest) Execute() (*ReflectResponse, *http.Response, error) {
|
||||
/*
|
||||
Reflect Reflect and generate answer
|
||||
|
||||
Reflect and formulate an answer using bank identity, world facts, and opinions.
|
||||
Reflect and formulate an answer using bank identity, world facts, observations, and mental models.
|
||||
|
||||
This endpoint:
|
||||
1. Retrieves experience (conversations and events)
|
||||
2. Retrieves world facts relevant to the query
|
||||
3. Retrieves existing opinions (bank's perspectives)
|
||||
3. Retrieves observations and mental models (bank's synthesized perspectives)
|
||||
4. Uses LLM to formulate a contextual answer
|
||||
5. Returns plain text answer and the facts used
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ from hindsight_client_api.models.bank_profile_response import BankProfileRespons
|
||||
from hindsight_client_api.models.file_retain_response import FileRetainResponse
|
||||
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse
|
||||
from hindsight_client_api.models.recall_response import RecallResponse
|
||||
from hindsight_client_api.models.recall_result import RecallResult
|
||||
from hindsight_client_api.models.reflect_response import ReflectResponse
|
||||
from hindsight_client_api.models.retain_response import RetainResponse
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class MemoryApi:
|
||||
async def clear_bank_memories(
|
||||
self,
|
||||
bank_id: StrictStr,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, opinion)")] = None,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, observation)")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
@@ -71,11 +71,11 @@ class MemoryApi:
|
||||
) -> DeleteResponse:
|
||||
"""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.
|
||||
Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
:param type: Optional fact type filter (world, experience, opinion)
|
||||
:param type: Optional fact type filter (world, experience, observation)
|
||||
:type type: str
|
||||
:param authorization:
|
||||
:type authorization: str
|
||||
@@ -130,7 +130,7 @@ class MemoryApi:
|
||||
async def clear_bank_memories_with_http_info(
|
||||
self,
|
||||
bank_id: StrictStr,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, opinion)")] = None,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, observation)")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
@@ -147,11 +147,11 @@ class MemoryApi:
|
||||
) -> ApiResponse[DeleteResponse]:
|
||||
"""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.
|
||||
Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
:param type: Optional fact type filter (world, experience, opinion)
|
||||
:param type: Optional fact type filter (world, experience, observation)
|
||||
:type type: str
|
||||
:param authorization:
|
||||
:type authorization: str
|
||||
@@ -206,7 +206,7 @@ class MemoryApi:
|
||||
async def clear_bank_memories_without_preload_content(
|
||||
self,
|
||||
bank_id: StrictStr,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, opinion)")] = None,
|
||||
type: Annotated[Optional[StrictStr], Field(description="Optional fact type filter (world, experience, observation)")] = None,
|
||||
authorization: Optional[StrictStr] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
@@ -223,11 +223,11 @@ class MemoryApi:
|
||||
) -> RESTResponseType:
|
||||
"""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.
|
||||
Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
:param type: Optional fact type filter (world, experience, opinion)
|
||||
:param type: Optional fact type filter (world, experience, observation)
|
||||
:type type: str
|
||||
:param authorization:
|
||||
:type authorization: str
|
||||
@@ -665,7 +665,7 @@ class MemoryApi:
|
||||
) -> GraphDataResponse:
|
||||
"""Get memory graph data
|
||||
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
@@ -765,7 +765,7 @@ class MemoryApi:
|
||||
) -> ApiResponse[GraphDataResponse]:
|
||||
"""Get memory graph data
|
||||
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
@@ -865,7 +865,7 @@ class MemoryApi:
|
||||
) -> RESTResponseType:
|
||||
"""Get memory graph data
|
||||
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).
|
||||
Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
@@ -2692,7 +2692,7 @@ class MemoryApi:
|
||||
) -> ReflectResponse:
|
||||
"""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. Returns plain text answer and the facts used
|
||||
Reflect and formulate an answer using bank identity, world facts, observations, and mental models. This endpoint: 1. Retrieves experience (conversations and events) 2. Retrieves world facts relevant to the query 3. Retrieves observations and mental models (bank's synthesized perspectives) 4. Uses LLM to formulate a contextual answer 5. Returns plain text answer and the facts used
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
@@ -2768,7 +2768,7 @@ class MemoryApi:
|
||||
) -> ApiResponse[ReflectResponse]:
|
||||
"""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. Returns plain text answer and the facts used
|
||||
Reflect and formulate an answer using bank identity, world facts, observations, and mental models. This endpoint: 1. Retrieves experience (conversations and events) 2. Retrieves world facts relevant to the query 3. Retrieves observations and mental models (bank's synthesized perspectives) 4. Uses LLM to formulate a contextual answer 5. Returns plain text answer and the facts used
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
@@ -2844,7 +2844,7 @@ class MemoryApi:
|
||||
) -> RESTResponseType:
|
||||
"""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. Returns plain text answer and the facts used
|
||||
Reflect and formulate an answer using bank identity, world facts, observations, and mental models. This endpoint: 1. Retrieves experience (conversations and events) 2. Retrieves world facts relevant to the query 3. Retrieves observations and mental models (bank's synthesized perspectives) 4. Uses LLM to formulate a contextual answer 5. Returns plain text answer and the facts used
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
|
||||
@@ -284,7 +284,7 @@ export const metricsEndpointMetricsGet = <ThrowOnError extends boolean = false>(
|
||||
/**
|
||||
* Get memory graph data
|
||||
*
|
||||
* Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).
|
||||
* Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).
|
||||
*/
|
||||
export const getGraph = <ThrowOnError extends boolean = false>(
|
||||
options: Options<GetGraphData, ThrowOnError>
|
||||
@@ -375,12 +375,12 @@ export const recallMemories = <ThrowOnError extends boolean = false>(
|
||||
/**
|
||||
* Reflect and generate answer
|
||||
*
|
||||
* Reflect and formulate an answer using bank identity, world facts, and opinions.
|
||||
* Reflect and formulate an answer using bank identity, world facts, observations, and mental models.
|
||||
*
|
||||
* This endpoint:
|
||||
* 1. Retrieves experience (conversations and events)
|
||||
* 2. Retrieves world facts relevant to the query
|
||||
* 3. Retrieves existing opinions (bank's perspectives)
|
||||
* 3. Retrieves observations and mental models (bank's synthesized perspectives)
|
||||
* 4. Uses LLM to formulate a contextual answer
|
||||
* 5. Returns plain text answer and the facts used
|
||||
*/
|
||||
@@ -1243,7 +1243,7 @@ export const listWebhookDeliveries = <ThrowOnError extends boolean = false>(
|
||||
/**
|
||||
* 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.
|
||||
* Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
|
||||
*/
|
||||
export const clearBankMemories = <ThrowOnError extends boolean = false>(
|
||||
options: Options<ClearBankMemoriesData, ThrowOnError>
|
||||
|
||||
@@ -6603,7 +6603,7 @@ export type ClearBankMemoriesData = {
|
||||
/**
|
||||
* Type
|
||||
*
|
||||
* Optional fact type filter (world, experience, opinion)
|
||||
* Optional fact type filter (world, experience, observation)
|
||||
*/
|
||||
type?: string | null;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
|
||||
@@ -1425,7 +1425,6 @@ function MapFieldsEditor({
|
||||
};
|
||||
|
||||
const isRoot = depth === 0;
|
||||
const indent = `${(depth + 1) * 12}px`;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -7,20 +7,11 @@ import remarkGfm from "remark-gfm";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { client } from "@/lib/api";
|
||||
import { useBank } from "@/lib/bank-context";
|
||||
import { useFeatures } from "@/lib/features-context";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { toast } from "sonner";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -39,24 +30,10 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
RefreshCw,
|
||||
Save,
|
||||
Brain,
|
||||
Clock,
|
||||
AlertCircle,
|
||||
CheckCircle,
|
||||
Database,
|
||||
Link2,
|
||||
FolderOpen,
|
||||
Activity,
|
||||
Trash2,
|
||||
Target,
|
||||
AlertTriangle,
|
||||
@@ -64,7 +41,6 @@ import {
|
||||
Tag,
|
||||
Loader2,
|
||||
X,
|
||||
MoreVertical,
|
||||
Pencil,
|
||||
} from "lucide-react";
|
||||
|
||||
@@ -104,16 +80,6 @@ interface BankStats {
|
||||
total_observations: number;
|
||||
}
|
||||
|
||||
interface Operation {
|
||||
id: string;
|
||||
task_type: string;
|
||||
items_count: number;
|
||||
document_id: string | null;
|
||||
created_at: string;
|
||||
status: string;
|
||||
error_message: string | null;
|
||||
}
|
||||
|
||||
interface Directive {
|
||||
id: string;
|
||||
bank_id: string;
|
||||
@@ -168,14 +134,9 @@ export function BankProfileView({ hideReflectFields = false }: { hideReflectFiel
|
||||
const tBank = useTranslations("bank");
|
||||
const traitLabels = useTraitLabels();
|
||||
const { currentBank, setCurrentBank, loadBanks } = useBank();
|
||||
const { features } = useFeatures();
|
||||
const observationsEnabled = features?.observations ?? false;
|
||||
const [profile, setProfile] = useState<BankProfile | null>(null);
|
||||
const [stats, setStats] = useState<BankStats | null>(null);
|
||||
const [operations, setOperations] = useState<Operation[]>([]);
|
||||
const [totalOperations, setTotalOperations] = useState(0);
|
||||
const [directives, setDirectives] = useState<Directive[]>([]);
|
||||
const [mentalModelsCount, setMentalModelsCount] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showDispositionDialog, setShowDispositionDialog] = useState(false);
|
||||
const [showMissionDialog, setShowMissionDialog] = useState(false);
|
||||
@@ -197,49 +158,19 @@ export function BankProfileView({ hideReflectFields = false }: { hideReflectFiel
|
||||
const [showClearObservationsDialog, setShowClearObservationsDialog] = useState(false);
|
||||
const [isClearingObservations, setIsClearingObservations] = useState(false);
|
||||
|
||||
// Consolidation state
|
||||
const [isConsolidating, setIsConsolidating] = useState(false);
|
||||
|
||||
// Operations filter/pagination state
|
||||
const [opsStatusFilter, setOpsStatusFilter] = useState<string | null>(null);
|
||||
const [opsLimit] = useState(10);
|
||||
const [opsOffset, setOpsOffset] = useState(0);
|
||||
const [cancellingOpId, setCancellingOpId] = useState<string | null>(null);
|
||||
|
||||
const loadOperations = async (
|
||||
statusFilter: string | null = opsStatusFilter,
|
||||
offset: number = opsOffset
|
||||
) => {
|
||||
if (!currentBank) return;
|
||||
try {
|
||||
const opsData = await client.listOperations(currentBank, {
|
||||
status: statusFilter || undefined,
|
||||
limit: opsLimit,
|
||||
offset,
|
||||
});
|
||||
setOperations(opsData.operations || []);
|
||||
setTotalOperations(opsData.total || 0);
|
||||
} catch (error) {
|
||||
console.error("Error loading operations:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadData = async (isPolling = false) => {
|
||||
if (!currentBank) return;
|
||||
|
||||
// During polling, only refresh stats (not operations to avoid interfering with filters)
|
||||
// During polling, only refresh read-only profile data to avoid overwriting form edits.
|
||||
// Use ref to get current value (avoids stale closure in setInterval)
|
||||
if (isPolling) {
|
||||
try {
|
||||
const [statsData, directivesData, mentalModelsData] = await Promise.all([
|
||||
const [statsData, directivesData] = await Promise.all([
|
||||
client.getBankStats(currentBank),
|
||||
client.listDirectives(currentBank),
|
||||
client.listMentalModels(currentBank),
|
||||
]);
|
||||
setStats(statsData as BankStats);
|
||||
setDirectives(directivesData.items || []);
|
||||
setMentalModelsCount(mentalModelsData.items?.length || 0);
|
||||
// Skip operations refresh during polling to not interfere with filter/pagination state
|
||||
} catch (error) {
|
||||
console.error("Error refreshing stats:", error);
|
||||
}
|
||||
@@ -248,17 +179,14 @@ export function BankProfileView({ hideReflectFields = false }: { hideReflectFiel
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const [profileData, statsData, directivesData, mentalModelsData] = await Promise.all([
|
||||
const [profileData, statsData, directivesData] = await Promise.all([
|
||||
client.getBankProfile(currentBank),
|
||||
client.getBankStats(currentBank),
|
||||
client.listDirectives(currentBank),
|
||||
client.listMentalModels(currentBank),
|
||||
]);
|
||||
setProfile(profileData);
|
||||
setStats(statsData as BankStats);
|
||||
setDirectives(directivesData.items || []);
|
||||
setMentalModelsCount(mentalModelsData.items?.length || 0);
|
||||
await loadOperations();
|
||||
} catch (error) {
|
||||
// Error toast is shown automatically by the API client interceptor
|
||||
} finally {
|
||||
@@ -301,47 +229,6 @@ export function BankProfileView({ hideReflectFields = false }: { hideReflectFiel
|
||||
}
|
||||
};
|
||||
|
||||
const handleTriggerConsolidation = async () => {
|
||||
if (!currentBank) return;
|
||||
|
||||
setIsConsolidating(true);
|
||||
try {
|
||||
await client.triggerConsolidation(currentBank);
|
||||
// Reload to show the new operation in the list
|
||||
await loadData();
|
||||
await loadOperations();
|
||||
} catch (error) {
|
||||
// Error toast is shown automatically by the API client interceptor
|
||||
} finally {
|
||||
setIsConsolidating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpsFilterChange = (newFilter: string | null) => {
|
||||
setOpsStatusFilter(newFilter);
|
||||
setOpsOffset(0); // Reset to first page when filter changes
|
||||
loadOperations(newFilter, 0);
|
||||
};
|
||||
|
||||
const handleOpsPageChange = (newOffset: number) => {
|
||||
setOpsOffset(newOffset);
|
||||
loadOperations(opsStatusFilter, newOffset);
|
||||
};
|
||||
|
||||
const handleCancelOperation = async (operationId: string) => {
|
||||
if (!currentBank) return;
|
||||
|
||||
setCancellingOpId(operationId);
|
||||
try {
|
||||
await client.cancelOperation(currentBank, operationId);
|
||||
await loadOperations();
|
||||
} catch (error) {
|
||||
// Error toast is shown automatically by the API client interceptor
|
||||
} finally {
|
||||
setCancellingOpId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteDirective = async () => {
|
||||
if (!currentBank || !directiveDeleteTarget) return;
|
||||
|
||||
@@ -361,7 +248,7 @@ export function BankProfileView({ hideReflectFields = false }: { hideReflectFiel
|
||||
useEffect(() => {
|
||||
if (currentBank) {
|
||||
loadData();
|
||||
// Refresh stats/operations every 5 seconds (isPolling=true to avoid overwriting form)
|
||||
// Refresh read-only profile data every 5 seconds without overwriting form state.
|
||||
const interval = setInterval(() => loadData(true), 5000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
|
||||
@@ -188,39 +188,6 @@ function ChartTooltip({ active, payload, label, valueLabel }: ChartTooltipProps)
|
||||
);
|
||||
}
|
||||
|
||||
function HeroCard({
|
||||
icon: Icon,
|
||||
label,
|
||||
value,
|
||||
pulse,
|
||||
}: {
|
||||
icon: typeof Database;
|
||||
label: string;
|
||||
value: number;
|
||||
pulse?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 rounded-md bg-muted">
|
||||
<Icon
|
||||
className={`w-4 h-4 ${pulse ? "animate-pulse text-amber-500" : "text-muted-foreground"}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-xs text-muted-foreground font-medium">{label}</p>
|
||||
<CompactNumber
|
||||
value={value}
|
||||
className="text-2xl font-semibold text-foreground leading-tight tabular-nums block"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionHeading({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<h3 className="text-[11px] font-semibold text-muted-foreground uppercase tracking-[0.08em] mb-3">
|
||||
|
||||
@@ -23,12 +23,6 @@ interface PreparedNode {
|
||||
linkCount: number;
|
||||
}
|
||||
|
||||
interface ScreenNode {
|
||||
idx: number;
|
||||
sx: number;
|
||||
sy: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Props
|
||||
// ============================================================================
|
||||
@@ -82,19 +76,6 @@ function lerp(a: number, b: number, t: number): number {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
function easeOutCubic(t: number): number {
|
||||
return 1 - Math.pow(1 - t, 3);
|
||||
}
|
||||
|
||||
function hexToRgba(hex: string, alpha: number): string {
|
||||
// Handle non-hex formats
|
||||
if (!hex.startsWith("#")) return hex;
|
||||
const r = parseInt(hex.slice(1, 3), 16);
|
||||
const g = parseInt(hex.slice(3, 5), 16);
|
||||
const b = parseInt(hex.slice(5, 7), 16);
|
||||
return `rgba(${r},${g},${b},${alpha})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a 0..1 value to a perceptually monotonic cool→warm ramp.
|
||||
* 0 = cool blue (low / older / few links), 1 = warm orange-red (high / newer / many).
|
||||
@@ -163,7 +144,6 @@ function useIsDarkMode() {
|
||||
// Constants
|
||||
// ============================================================================
|
||||
|
||||
const FONT = '12px Inter, -apple-system, "Segoe UI", sans-serif';
|
||||
const FONT_SMALL = '11px Inter, -apple-system, "Segoe UI", sans-serif';
|
||||
const FONT_BOLD = '600 10px Inter, -apple-system, "Segoe UI", sans-serif';
|
||||
const MONO = '11px "SF Mono", "Fira Code", Consolas, monospace';
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import { Circle } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const RadioGroup = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return <RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />;
|
||||
});
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
||||
|
||||
const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
);
|
||||
});
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||
|
||||
export { RadioGroup, RadioGroupItem };
|
||||
@@ -1,122 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Sheet = SheetPrimitive.Root;
|
||||
|
||||
const SheetTrigger = SheetPrimitive.Trigger;
|
||||
|
||||
const SheetClose = SheetPrimitive.Close;
|
||||
|
||||
const SheetPortal = SheetPrimitive.Portal;
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
interface SheetContentProps
|
||||
extends
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
));
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||
|
||||
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
||||
);
|
||||
SheetHeader.displayName = "SheetHeader";
|
||||
|
||||
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetFooter.displayName = "SheetFooter";
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetPortal,
|
||||
SheetOverlay,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
@@ -1,47 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect } from "react";
|
||||
import { client } from "./api";
|
||||
|
||||
interface AgentContextType {
|
||||
currentAgent: string | null;
|
||||
setCurrentAgent: (agent: string | null) => void;
|
||||
agents: string[];
|
||||
loadAgents: () => Promise<void>;
|
||||
}
|
||||
|
||||
const AgentContext = createContext<AgentContextType | undefined>(undefined);
|
||||
|
||||
export function AgentProvider({ children }: { children: React.ReactNode }) {
|
||||
const [currentAgent, setCurrentAgent] = useState<string | null>(null);
|
||||
const [agents, setAgents] = useState<string[]>([]);
|
||||
|
||||
const loadAgents = async () => {
|
||||
try {
|
||||
const data = await client.listBanks();
|
||||
// Extract bank_id from each bank object
|
||||
const agentIds = data.banks?.map((agent: any) => agent.bank_id) || [];
|
||||
setAgents(agentIds);
|
||||
} catch (error) {
|
||||
console.error("Error loading agents:", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadAgents();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AgentContext.Provider value={{ currentAgent, setCurrentAgent, agents, loadAgents }}>
|
||||
{children}
|
||||
</AgentContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAgent() {
|
||||
const context = useContext(AgentContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useAgent must be used within an AgentProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@@ -6,7 +6,6 @@ This script imports the FastAPI app and exports its OpenAPI schema to a JSON fil
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ Conventions in cookbook repo:
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
@@ -246,33 +246,6 @@ response = client.recall(
|
||||
# [/docs:recall-tags-all-strict]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Legacy snippets for v0.3 docs (kept for backward compatibility)
|
||||
# =============================================================================
|
||||
|
||||
# [docs:recall-opinions-only]
|
||||
# Legacy: opinions replaced by observations in v0.4+
|
||||
# Only retrieve opinions (beliefs and preferences)
|
||||
opinions = client.recall(
|
||||
bank_id="my-bank",
|
||||
query="What are my preferences?",
|
||||
types=["opinion"]
|
||||
)
|
||||
# [/docs:recall-opinions-only]
|
||||
|
||||
|
||||
# [docs:recall-include-entities]
|
||||
# Legacy: entity summaries replaced by observations in v0.4+
|
||||
# Include entity summaries in recall results
|
||||
response = client.recall(
|
||||
bank_id="my-bank",
|
||||
query="What do I know about Alice?",
|
||||
include_entities=True,
|
||||
max_entity_tokens=500
|
||||
)
|
||||
# [/docs:recall-include-entities]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Cleanup (not shown in docs)
|
||||
# =============================================================================
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function CopyPageButton(): JSX.Element | null {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const copyPageAsMarkdown = useCallback(async () => {
|
||||
try {
|
||||
// Get the page content
|
||||
const contentElement = document.querySelector('.markdown');
|
||||
if (!contentElement) return;
|
||||
|
||||
// Convert HTML to markdown-like text
|
||||
let markdown = '';
|
||||
|
||||
// Add title
|
||||
const title = document.querySelector('h1')?.textContent;
|
||||
if (title) {
|
||||
markdown += `# ${title}\n\n`;
|
||||
}
|
||||
|
||||
// Extract text content from the markdown container
|
||||
const extractMarkdown = (element: Element): string => {
|
||||
let text = '';
|
||||
|
||||
const processNode = (node: Node): string => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
return node.textContent || '';
|
||||
}
|
||||
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
const el = node as Element;
|
||||
const tagName = el.tagName.toLowerCase();
|
||||
const children = Array.from(el.childNodes).map(processNode).join('');
|
||||
|
||||
switch (tagName) {
|
||||
case 'h1':
|
||||
return `# ${children}\n\n`;
|
||||
case 'h2':
|
||||
return `## ${children}\n\n`;
|
||||
case 'h3':
|
||||
return `### ${children}\n\n`;
|
||||
case 'h4':
|
||||
return `#### ${children}\n\n`;
|
||||
case 'h5':
|
||||
return `##### ${children}\n\n`;
|
||||
case 'h6':
|
||||
return `###### ${children}\n\n`;
|
||||
case 'p':
|
||||
return `${children}\n\n`;
|
||||
case 'ul':
|
||||
return `${children}\n`;
|
||||
case 'ol':
|
||||
return `${children}\n`;
|
||||
case 'li':
|
||||
const parent = el.parentElement;
|
||||
const isOrdered = parent?.tagName.toLowerCase() === 'ol';
|
||||
if (isOrdered) {
|
||||
const index = Array.from(parent?.children || []).indexOf(el) + 1;
|
||||
return `${index}. ${children}\n`;
|
||||
}
|
||||
return `- ${children}\n`;
|
||||
case 'code':
|
||||
const isBlock = el.parentElement?.tagName.toLowerCase() === 'pre';
|
||||
if (isBlock) {
|
||||
const lang = el.className.replace('language-', '');
|
||||
return `\`\`\`${lang}\n${children}\n\`\`\`\n\n`;
|
||||
}
|
||||
return `\`${children}\``;
|
||||
case 'pre':
|
||||
return children; // Already handled by code block
|
||||
case 'blockquote':
|
||||
return children.split('\n').map(line => `> ${line}`).join('\n') + '\n\n';
|
||||
case 'a':
|
||||
const href = el.getAttribute('href') || '';
|
||||
return `[${children}](${href})`;
|
||||
case 'strong':
|
||||
case 'b':
|
||||
return `**${children}**`;
|
||||
case 'em':
|
||||
case 'i':
|
||||
return `*${children}*`;
|
||||
case 'br':
|
||||
return '\n';
|
||||
case 'hr':
|
||||
return '---\n\n';
|
||||
case 'table':
|
||||
return `${children}\n`;
|
||||
case 'thead':
|
||||
case 'tbody':
|
||||
return children;
|
||||
case 'tr':
|
||||
return `${children}|\n`;
|
||||
case 'th':
|
||||
case 'td':
|
||||
return `| ${children} `;
|
||||
case 'img':
|
||||
const src = el.getAttribute('src') || '';
|
||||
const alt = el.getAttribute('alt') || '';
|
||||
return ``;
|
||||
default:
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
Array.from(element.childNodes).forEach(node => {
|
||||
text += processNode(node);
|
||||
});
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
// Skip the title h1 if it's already added
|
||||
const contentToCopy = Array.from(contentElement.children)
|
||||
.filter(child => !(child.tagName === 'H1' && child.textContent === title))
|
||||
.map(child => extractMarkdown(child))
|
||||
.join('');
|
||||
|
||||
markdown += contentToCopy;
|
||||
|
||||
// Clean up excessive newlines
|
||||
markdown = markdown.replace(/\n{3,}/g, '\n\n').trim();
|
||||
|
||||
// Copy to clipboard
|
||||
await navigator.clipboard.writeText(markdown);
|
||||
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (error) {
|
||||
console.error('Failed to copy page content:', error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${styles.copyPageButton} ${copied ? styles.copied : ''}`}
|
||||
onClick={copyPageAsMarkdown}
|
||||
aria-label="Copy page as markdown"
|
||||
title="Copy page as markdown"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425a.247.247 0 0 1 .02-.022Z"/>
|
||||
</svg>
|
||||
<span className={styles.buttonText}>Copied!</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H6z"/>
|
||||
<path d="M2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1H2z"/>
|
||||
</svg>
|
||||
<span className={styles.buttonText}>Copy page</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
.copyPageButton {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
border-radius: 6px;
|
||||
color: var(--ifm-font-color-base);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.copyPageButton:hover {
|
||||
background-color: var(--ifm-color-emphasis-100);
|
||||
border-color: var(--ifm-color-emphasis-400);
|
||||
}
|
||||
|
||||
.copyPageButton:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.copyPageButton.copied {
|
||||
background-color: var(--ifm-color-success-contrast-background);
|
||||
border-color: var(--ifm-color-success);
|
||||
color: var(--ifm-color-success-darkest);
|
||||
}
|
||||
|
||||
.copyPageButton.copied:hover {
|
||||
background-color: var(--ifm-color-success-contrast-background);
|
||||
border-color: var(--ifm-color-success);
|
||||
}
|
||||
|
||||
.buttonText {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
/* Dark mode adjustments */
|
||||
[data-theme='dark'] .copyPageButton {
|
||||
border-color: var(--ifm-color-emphasis-400);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .copyPageButton:hover {
|
||||
background-color: var(--ifm-color-emphasis-200);
|
||||
border-color: var(--ifm-color-emphasis-500);
|
||||
}
|
||||
|
||||
[data-theme='dark'] .copyPageButton.copied {
|
||||
background-color: var(--ifm-color-success-dark);
|
||||
border-color: var(--ifm-color-success);
|
||||
color: var(--ifm-color-success-contrast-foreground);
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
.toastContainer {
|
||||
position: fixed;
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toastContainer.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.toast {
|
||||
background: linear-gradient(135deg, #0074d9 0%, #005db0 100%);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 16px 20px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
max-width: 420px;
|
||||
min-width: 320px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.command {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', Monaco, Consolas, monospace;
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: #e6f7f8;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s ease;
|
||||
padding: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.closeButton:hover {
|
||||
opacity: 1;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.closeButton:active {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 768px) {
|
||||
.toastContainer {
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
min-width: unset;
|
||||
max-width: unset;
|
||||
width: 100%;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.command {
|
||||
font-size: 11px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode adjustments (if needed) */
|
||||
html[data-theme='dark'] .toast {
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styles from './SkillToast.module.css';
|
||||
|
||||
const STORAGE_KEY = 'hindsight-skill-toast-dismissed';
|
||||
|
||||
export default function SkillToast(): JSX.Element | null {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has already dismissed the toast
|
||||
const dismissed = localStorage.getItem(STORAGE_KEY);
|
||||
|
||||
if (!dismissed) {
|
||||
// Show toast after a short delay
|
||||
const timer = setTimeout(() => {
|
||||
setIsVisible(true);
|
||||
setIsAnimating(true);
|
||||
}, 1500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleDismiss = () => {
|
||||
setIsAnimating(false);
|
||||
setTimeout(() => {
|
||||
setIsVisible(false);
|
||||
localStorage.setItem(STORAGE_KEY, 'true');
|
||||
}, 300); // Match animation duration
|
||||
};
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div className={`${styles.toastContainer} ${isAnimating ? styles.show : ''}`}>
|
||||
<div className={styles.toast}>
|
||||
<div className={styles.icon}>🤖</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.title}>Building with a coding agent?</div>
|
||||
<div className={styles.message}>
|
||||
Install the Hindsight documentation skill for faster development:
|
||||
</div>
|
||||
<code className={styles.command}>
|
||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash
|
||||
</code>
|
||||
</div>
|
||||
<button
|
||||
className={styles.closeButton}
|
||||
onClick={handleDismiss}
|
||||
aria-label="Dismiss notification"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,13 +6,13 @@ Hindsight is an agent memory system that gives AI agents persistent, structured
|
||||
|
||||
## Links
|
||||
|
||||
- [Full Documentation (llms-full.txt)](https://vectorize-io.github.io/hindsight/llms-full.txt): Complete documentation for LLM consumption
|
||||
- [Quick Start](https://vectorize-io.github.io/hindsight/developer/api/quickstart): Get started in 60 seconds
|
||||
- [Python SDK](https://vectorize-io.github.io/hindsight/sdks/python): Python client library
|
||||
- [TypeScript SDK](https://vectorize-io.github.io/hindsight/sdks/nodejs): Node.js/TypeScript client
|
||||
- [API Reference](https://vectorize-io.github.io/hindsight/api-reference): REST API documentation
|
||||
- [OpenAPI Spec](https://vectorize-io.github.io/hindsight/openapi.json): Machine-readable API specification
|
||||
- [MCP Server](https://vectorize-io.github.io/hindsight/sdks/mcp): Model Context Protocol integration
|
||||
- [Full Documentation (llms-full.txt)](https://hindsight.vectorize.io/llms-full.txt): Complete documentation for LLM consumption
|
||||
- [Quick Start](https://hindsight.vectorize.io/developer/api/quickstart): Get started in 60 seconds
|
||||
- [Python SDK](https://hindsight.vectorize.io/sdks/python): Python client library
|
||||
- [TypeScript SDK](https://hindsight.vectorize.io/sdks/nodejs): Node.js/TypeScript client
|
||||
- [API Reference](https://hindsight.vectorize.io/api-reference): REST API documentation
|
||||
- [OpenAPI Spec](https://hindsight.vectorize.io/openapi.json): Machine-readable API specification
|
||||
- [MCP Server](https://hindsight.vectorize.io/sdks/mcp): Model Context Protocol integration
|
||||
- [GitHub](https://github.com/vectorize-io/hindsight): Source code and issues
|
||||
|
||||
## Core Operations
|
||||
@@ -69,8 +69,8 @@ Base URL: `http://localhost:8888`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/v1/default/banks/{bank_id}/retain` | Store memories |
|
||||
| POST | `/v1/default/banks/{bank_id}/recall` | Retrieve memories |
|
||||
| POST | `/v1/default/banks/{bank_id}/memories` | Store memories |
|
||||
| POST | `/v1/default/banks/{bank_id}/memories/recall` | Retrieve memories |
|
||||
| POST | `/v1/default/banks/{bank_id}/reflect` | Analyze and form opinions |
|
||||
| GET | `/v1/default/banks/{bank_id}/profile` | Get bank profile |
|
||||
| PUT | `/v1/default/banks/{bank_id}/profile` | Update bank profile |
|
||||
@@ -81,11 +81,11 @@ Base URL: `http://localhost:8888`
|
||||
|
||||
### Per-User Memory
|
||||
One bank per user. Simplest pattern for chatbots and assistants.
|
||||
[Guide](https://vectorize-io.github.io/hindsight/cookbook/per-user-memory)
|
||||
[Guide](https://hindsight.vectorize.io/cookbook/per-user-memory)
|
||||
|
||||
### Support Agent + Shared Knowledge
|
||||
User bank + shared docs bank. Client orchestrates queries to both banks and merges results.
|
||||
[Guide](https://vectorize-io.github.io/hindsight/cookbook/support-agent-with-shared-knowledge)
|
||||
[Guide](https://hindsight.vectorize.io/cookbook/support-agent-with-shared-knowledge)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Get memory graph data",
|
||||
"description": "Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).",
|
||||
"description": "Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).",
|
||||
"operationId": "get_graph",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -677,7 +677,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Reflect and generate answer",
|
||||
"description": "Reflect and formulate an answer using bank identity, world facts, and opinions.\n\nThis endpoint:\n1. Retrieves experience (conversations and events)\n2. Retrieves world facts relevant to the query\n3. Retrieves existing opinions (bank's perspectives)\n4. Uses LLM to formulate a contextual answer\n5. Returns plain text answer and the facts used",
|
||||
"description": "Reflect and formulate an answer using bank identity, world facts, observations, and mental models.\n\nThis endpoint:\n1. Retrieves experience (conversations and events)\n2. Retrieves world facts relevant to the query\n3. Retrieves observations and mental models (bank's synthesized perspectives)\n4. Uses LLM to formulate a contextual answer\n5. Returns plain text answer and the facts used",
|
||||
"operationId": "reflect",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -4955,7 +4955,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Clear memory bank memories",
|
||||
"description": "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.",
|
||||
"description": "Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.",
|
||||
"operationId": "clear_bank_memories",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -4980,10 +4980,10 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional fact type filter (world, experience, opinion)",
|
||||
"description": "Optional fact type filter (world, experience, observation)",
|
||||
"title": "Type"
|
||||
},
|
||||
"description": "Optional fact type filter (world, experience, opinion)"
|
||||
"description": "Optional fact type filter (world, experience, observation)"
|
||||
},
|
||||
{
|
||||
"name": "authorization",
|
||||
|
||||
@@ -24,7 +24,7 @@ from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
|
||||
from .embed_manager import EmbedManager
|
||||
from .profile_manager import UI_PORT_OFFSET, ProfileManager, lock_file, resolve_active_profile, unlock_file
|
||||
from .profile_manager import UI_PORT_OFFSET, ProfileManager, lock_file, unlock_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
console = Console(stderr=True)
|
||||
@@ -614,7 +614,7 @@ class DaemonEmbedManager(EmbedManager):
|
||||
console.print()
|
||||
return False
|
||||
|
||||
except FileNotFoundError as e:
|
||||
except FileNotFoundError:
|
||||
error_msg = (
|
||||
f"Command not found: {cmd[0]}\nFull command: {' '.join(cmd)}\n\n"
|
||||
"Install hindsight-api with: pip install hindsight-api"
|
||||
|
||||
@@ -15,7 +15,6 @@ import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from lib.client import HindsightClient
|
||||
from lib.config import debug_log, load_config
|
||||
from lib.daemon import get_api_url, prestart_daemon_background
|
||||
|
||||
@@ -42,7 +41,6 @@ def main():
|
||||
|
||||
try:
|
||||
api_url = get_api_url(config, debug_fn=_dbg, allow_daemon_start=False)
|
||||
client = HindsightClient(api_url, config.get("hindsightApiToken"))
|
||||
debug_log(config, f"Hindsight server reachable at {api_url}")
|
||||
except (RuntimeError, ValueError) as e:
|
||||
# Server not running — kick off background pre-start so it's ready
|
||||
|
||||
@@ -17,7 +17,7 @@ import urllib.request
|
||||
|
||||
from .client import USER_AGENT
|
||||
from .llm import detect_llm_config, get_llm_env_vars
|
||||
from .state import read_state, write_state
|
||||
from .state import write_state
|
||||
|
||||
DAEMON_STATE_FILE = "daemon.json"
|
||||
PROFILE_NAME = "codex"
|
||||
|
||||
@@ -9,7 +9,6 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
DEFAULT_TIMEOUT = 15
|
||||
HEALTH_CHECK_RETRIES = 3
|
||||
|
||||
@@ -20,7 +20,7 @@ import urllib.request
|
||||
|
||||
from .client import USER_AGENT
|
||||
from .llm import detect_llm_config, get_llm_env_vars
|
||||
from .state import read_state, write_state
|
||||
from .state import write_state
|
||||
|
||||
DAEMON_STATE_FILE = "daemon.json"
|
||||
PROFILE_NAME = "cursor-cli"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -1519,7 +1519,6 @@ def hindsight_memory(
|
||||
# Save previous state
|
||||
was_enabled = is_enabled()
|
||||
previous_config = get_config()
|
||||
previous_defaults = get_defaults()
|
||||
|
||||
try:
|
||||
# Configure and enable
|
||||
|
||||
@@ -8,7 +8,6 @@ Provides automatic memory for LlamaIndex agents:
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ LlamaIndex-compatible tools backed by Hindsight's retain/recall/reflect APIs.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { existsSync, realpathSync } from "fs";
|
||||
import { dirname, join, resolve } from "path";
|
||||
import { createRequire } from "module";
|
||||
import { fileURLToPath, pathToFileURL } from "url";
|
||||
import { fileURLToPath } from "url";
|
||||
import { HindsightServer } from "@vectorize-io/hindsight-all";
|
||||
import { HindsightClient } from "@vectorize-io/hindsight-client";
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import { RetainQueue } from "./retain-queue.js";
|
||||
import { compileSessionPatterns, matchesSessionPattern } from "./session-patterns.js";
|
||||
import { createHash } from "crypto";
|
||||
import { dirname, join } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import * as log from "./logger.js";
|
||||
import { configureLogger, setApiLogger, stopLogger } from "./logger.js";
|
||||
import { mkdirSync } from "fs";
|
||||
@@ -458,10 +457,6 @@ if (typeof global !== "undefined") {
|
||||
};
|
||||
}
|
||||
|
||||
// Get directory of current module
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Default bank name (fallback when channel context not available)
|
||||
const DEFAULT_BANK_NAME = "openclaw";
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
applyApiMode,
|
||||
applyCloudMode,
|
||||
applyEmbeddedMode,
|
||||
defaultApiKeyEnvVar,
|
||||
ensurePluginConfig,
|
||||
isValidEnvVarName,
|
||||
loadConfig,
|
||||
@@ -275,9 +274,6 @@ export async function runNonInteractive(
|
||||
// Interactive (TUI) execution
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const validateEnvVar = (value: string | undefined): string | undefined =>
|
||||
isValidEnvVarName(value) ? undefined : "Must be an UPPER_SNAKE_CASE env var name";
|
||||
|
||||
const validateRequired =
|
||||
(msg: string) =>
|
||||
(value: string | undefined): string | undefined =>
|
||||
|
||||
@@ -14,7 +14,6 @@ import { Logger } from "./logger.js";
|
||||
import {
|
||||
formatMemories,
|
||||
formatCurrentTime,
|
||||
stripMemoryTags,
|
||||
composeRecallQuery,
|
||||
truncateRecallQuery,
|
||||
prepareRetentionTranscript,
|
||||
|
||||
Generated
-33
@@ -368,7 +368,6 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
@@ -8667,38 +8666,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-radio-group": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
|
||||
"integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-roving-focus": "1.1.11",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
||||
|
||||
@@ -10,7 +10,7 @@ BLUE='\033[0;34m'
|
||||
DIM='\033[2m'
|
||||
NC='\033[0m'
|
||||
|
||||
ALL_INTEGRATIONS=("litellm" "pydantic-ai" "crewai" "ag2" "ai-sdk" "chat" "openclaw" "langgraph" "llamaindex" "nemoclaw" "strands" "claude-code" "codex" "autogen" "paperclip" "opencode" "cloudflare-oauth-proxy")
|
||||
ALL_INTEGRATIONS=("ag2" "agentcore" "agno" "ai-sdk" "autogen" "chat" "claude-agent-sdk" "claude-code" "cline" "cloudflare-oauth-proxy" "codex" "crewai" "cursor" "cursor-cli" "dify" "flowise" "gemini-spark" "google-adk" "haystack" "langgraph" "litellm" "llamaindex" "n8n" "nemoclaw" "obsidian" "omo" "openai-agents" "openclaw" "opencode" "paperclip" "pipecat" "pydantic-ai" "roo-code" "smolagents" "strands" "superagent" "vapi")
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [integration]"
|
||||
|
||||
@@ -13,7 +13,7 @@ print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||
print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
VALID_INTEGRATIONS=("litellm" "pydantic-ai" "crewai" "ag2" "ai-sdk" "chat" "openclaw" "langgraph" "llamaindex" "nemoclaw" "strands" "claude-code" "codex" "cline" "cursor-cli" "hermes" "autogen" "paperclip" "opencode" "cursor" "cloudflare-oauth-proxy" "openai-agents" "pipecat" "agentcore" "smolagents" "n8n" "dify" "gemini-spark" "vapi" "roo-code" "flowise" "google-adk" "haystack" "claude-agent-sdk" "superagent" "omo" "obsidian")
|
||||
VALID_INTEGRATIONS=("ag2" "agentcore" "agno" "ai-sdk" "autogen" "chat" "claude-agent-sdk" "claude-code" "cline" "cloudflare-oauth-proxy" "codex" "crewai" "cursor" "cursor-cli" "dify" "flowise" "gemini-spark" "google-adk" "haystack" "langgraph" "litellm" "llamaindex" "n8n" "nemoclaw" "obsidian" "omo" "openai-agents" "openclaw" "opencode" "paperclip" "pipecat" "pydantic-ai" "roo-code" "smolagents" "strands" "superagent" "vapi")
|
||||
|
||||
usage() {
|
||||
print_error "Usage: $0 <integration> <version>"
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Get memory graph data",
|
||||
"description": "Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion).",
|
||||
"description": "Retrieve graph data for visualization, optionally filtered by type (world/experience/observation).",
|
||||
"operationId": "get_graph",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -677,7 +677,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Reflect and generate answer",
|
||||
"description": "Reflect and formulate an answer using bank identity, world facts, and opinions.\n\nThis endpoint:\n1. Retrieves experience (conversations and events)\n2. Retrieves world facts relevant to the query\n3. Retrieves existing opinions (bank's perspectives)\n4. Uses LLM to formulate a contextual answer\n5. Returns plain text answer and the facts used",
|
||||
"description": "Reflect and formulate an answer using bank identity, world facts, observations, and mental models.\n\nThis endpoint:\n1. Retrieves experience (conversations and events)\n2. Retrieves world facts relevant to the query\n3. Retrieves observations and mental models (bank's synthesized perspectives)\n4. Uses LLM to formulate a contextual answer\n5. Returns plain text answer and the facts used",
|
||||
"operationId": "reflect",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -4955,7 +4955,7 @@
|
||||
"Memory"
|
||||
],
|
||||
"summary": "Clear memory bank memories",
|
||||
"description": "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.",
|
||||
"description": "Delete memory units for a memory bank. Optionally filter by type (world, experience, observation) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.",
|
||||
"operationId": "clear_bank_memories",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -4980,10 +4980,10 @@
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Optional fact type filter (world, experience, opinion)",
|
||||
"description": "Optional fact type filter (world, experience, observation)",
|
||||
"title": "Type"
|
||||
},
|
||||
"description": "Optional fact type filter (world, experience, opinion)"
|
||||
"description": "Optional fact type filter (world, experience, observation)"
|
||||
},
|
||||
{
|
||||
"name": "authorization",
|
||||
|
||||
Reference in New Issue
Block a user