From 30a319a6abd4ea04b0bc750d3f51e49db82b9059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Thu, 2 Apr 2026 12:21:53 +0200 Subject: [PATCH] feat: bank template import/export with Template Hub (#819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(api): add bank template import/export endpoints Add POST /banks/{bank_id}/import and GET /banks/{bank_id}/export endpoints for declarative bank setup via JSON manifests. A template manifest (version 1) can include bank config overrides and mental model definitions. Import creates or updates mental models matched by id, applies config as per-bank overrides, and returns async operation IDs for content generation. Export dumps a bank's explicit overrides and mental models as a manifest that can be re-imported into another bank. Includes control plane UI: bank creation dialog now accepts an optional template JSON to pre-configure the bank on creation. * docs: add Template Gallery page and bank templates reference - Template Gallery (/templates) with search, category filter, manifest preview modal with copy-to-clipboard - 5 starter templates: Customer Support, Research Assistant, Personal Journal, Code Review Buddy, Meeting Notes - Bank Templates API reference doc (developer/api/bank-templates) - Sidebar entry under API section * docs: add Template Gallery links to navbar and sidebar - Top navbar: "Templates" link between Integrations and Changelog - Sidebar: "Template Gallery" in Resources section * fix(docs): remove emoji icons, autofocus search, fix placeholder in template gallery * docs: rename to Bank Templates, move to Resources sidebar only * docs: add Bank Templates to Resources navbar dropdown * feat(api): add directives to bank template import/export - Add BankTemplateDirective model with name, content, priority, is_active, tags - Import creates/updates directives matched by name - Export includes all directives (active and inactive) - Validation: duplicate names rejected, empty name/content caught - Tests: 24 tests covering directives create/update, existing vs new bank import, validation, export with directives, full round-trip * docs: add directives to bank templates docs and sample templates * feat(api): add JSON Schema endpoint for bank template validation - GET /v1/default/bank-template-schema returns the JSON Schema auto-generated from the Pydantic BankTemplateManifest model - Static schema file at docs/static/bank-template-schema.json - Docs updated with schema endpoint, static file link, and validation examples (Python jsonschema, Node ajv-cli) * feat(api): live schema validation on import, fix schema endpoint path - Move schema endpoint to /v1/bank-template-schema (system-level, not per-bank) - Import endpoint now accepts raw JSON and validates with Pydantic manually, returning clean 400 errors instead of raw 422s for all validation failures - All validation (schema + semantic) returns consistent 400 with detailed messages * docs: add interactive JSON Schema viewer to Bank Templates page Renders the Pydantic-generated schema as a collapsible property tree with types, required badges, defaults, and descriptions. The schema is imported from the static bank-template-schema.json file. * ui: add template toggle switch and browse link to bank creation dialog - Replace always-visible textarea with a switch toggle ("Import from template") - Textarea only shows when switch is on, keeping the dialog clean by default - Add "Browse templates" link pointing to hindsight.vectorize.io/templates - Reset template state when switch is toggled off or dialog is cancelled * ui: add empty state with Add Document CTA to data view When a bank has 0 memories, the data view (all tabs: constellation, graph, table, timeline) shows a centered empty state with a CTA button that opens the Add Document dialog. * docs: replace templates with Conversation and Coding Agent Remove generic placeholder templates. Add two practical templates based on actual integration patterns: - Conversation: for chat agents (LiteLLM, LangGraph, Pydantic AI, Vercel AI SDK). Tracks user preferences, open threads. - Coding Agent: for Claude Code/Codex. Tracks technical decisions, project context, developer preferences. High literalism. * docs: rename gallery to Bank Templates Hub, keep API doc as Bank Templates * docs: register layout-template and file-json icons in navbar and sidebar * docs: register layout-template icon in DefaultNavbarItem for dropdown items * docs: show integration icons on template cards Templates now have an optional `integrations` field referencing integration IDs from integrations.json. Icons are resolved at render time and shown in the card header next to the category badge. * docs: add Personal Assistant template for OpenClaw, Hermes, NemoClaw * feat: add Export Template to bank actions + map all integrations to templates - Add "Export Template" to the bank Actions dropdown — exports config, mental models, and directives as JSON, copies to clipboard - Add export API route and client method - Map remaining integrations to templates: CrewAI, AG2, Agno, Strands, LlamaIndex, local-mcp, skills → Conversation; hindclaw → Personal Assistant * feat: add --template flag to LoCoMo benchmark + remove schema from Hub - LoCoMo benchmark accepts --template to apply a bank template manifest (config, mental models, directives) before ingestion - Template is applied per-bank in both single-phase and two-phase modes - BenchmarkRunner.apply_template() reuses the same engine methods as the /import API endpoint - Remove Manifest Schema section from Bank Templates Hub page (schema stays in the API reference doc) * refactor: remove description field from bank template manifest * docs: remove tags, fact_types, and directives from starter templates * docs: remove reflect_mission and disposition fields from starter templates * build: validate template manifests against JSON Schema during docs build * cleanup: remove unused JsonSchemaViewer component * docs: remove retain_extraction_mode from starter templates * ui: enable word wrap in template manifest preview * docs: add link to Bank Templates reference doc from Hub page * docs: convert bank templates doc to mdx with multi-language code snippets - Convert bank-templates.md to .mdx with Tabs/CodeSnippet components - Add example files: bank-templates.py, .mjs, .sh, .go with doc markers - Examples cover import, dry-run, export, round-trip, and schema - Regenerate OpenAPI spec and all client SDKs (Python, TS, Rust, Go) * fix: migration revision collision + use typed models in benchmark template - Rename merge migration d6e7f8a9b0c1 -> d6e7f8a9b0c2 to resolve revision ID collision with case_insensitive_entities_trgm_index - Update a4b5c6d7e8f9 down_revision to point to the renamed migration - Fix f-string lint in case_insensitive migration - BenchmarkRunner.apply_template() now validates manifest through BankTemplateManifest Pydantic model instead of raw dict access - Remove redundant inline imports (json, Path already at module top) * fix(docs): add missing Go tab to dry-run code snippet * ci: retrigger * fix: sync skills openapi.json + fix bankId null type error in export - Copy updated openapi.json to skills/hindsight-docs/references/ - Add null guard for bankId in Export Template onClick handler * fix: sync generated files (memory_engine formatting, docs skill references) * cleanup: remove obsolete migration collision workaround --- ...fc_case_insensitive_entities_trgm_index.py | 12 +- ...c6d7e8f9_fix_per_bank_vector_index_type.py | 7 +- ...f8a9b0c1_drop_documents_metadata_column.py | 34 - hindsight-api-slim/hindsight_api/api/http.py | 481 +++++++++- .../hindsight_api/engine/memory_engine.py | 90 +- .../tests/test_bank_templates.py | 600 ++++++++++++ hindsight-clients/go/api/openapi.yaml | 341 +++++++ hindsight-clients/go/api_bank_templates.go | 380 ++++++++ hindsight-clients/go/client.go | 3 + .../go/model_bank_template_config.go | 633 +++++++++++++ .../go/model_bank_template_directive.go | 307 +++++++ .../go/model_bank_template_import_response.go | 414 +++++++++ .../go/model_bank_template_manifest.go | 279 ++++++ .../go/model_bank_template_mental_model.go | 332 +++++++ .../python/.openapi-generator/FILES | 6 + .../python/hindsight_client_api/__init__.py | 6 + .../hindsight_client_api/api/__init__.py | 1 + .../api/bank_templates_api.py | 858 ++++++++++++++++++ .../hindsight_client_api/models/__init__.py | 5 + .../models/bank_template_config.py | 170 ++++ .../models/bank_template_directive.py | 95 ++ .../models/bank_template_import_response.py | 101 +++ .../models/bank_template_manifest.py | 128 +++ .../models/bank_template_mental_model.py | 102 +++ .../typescript/generated/sdk.gen.ts | 50 + .../typescript/generated/types.gen.ts | 352 +++++++ .../app/api/banks/[bankId]/export/route.ts | 26 + .../app/api/banks/[bankId]/import/route.ts | 31 + .../src/app/banks/[bankId]/page.tsx | 19 +- .../src/components/bank-selector.tsx | 88 +- .../src/components/data-view.tsx | 22 +- hindsight-control-plane/src/lib/api.ts | 27 + .../benchmarks/common/benchmark_runner.py | 70 ++ .../benchmarks/locomo/locomo_benchmark.py | 9 + .../docs/developer/api/bank-templates.mdx | 251 +++++ hindsight-docs/docusaurus.config.ts | 5 + hindsight-docs/examples/api/bank-templates.go | 97 ++ .../examples/api/bank-templates.mjs | 99 ++ hindsight-docs/examples/api/bank-templates.py | 95 ++ hindsight-docs/examples/api/bank-templates.sh | 63 ++ hindsight-docs/package.json | 2 +- hindsight-docs/scripts/check-templates.mjs | 53 ++ hindsight-docs/sidebars.ts | 12 + hindsight-docs/src/data/templates.json | 130 +++ .../src/pages/templates/index.module.css | 569 ++++++++++++ hindsight-docs/src/pages/templates/index.tsx | 219 +++++ .../src/theme/DocSidebarItem/Link/index.tsx | 3 + .../NavbarItem/DefaultNavbarItem/index.tsx | 5 +- .../NavbarItem/DropdownNavbarItem/index.tsx | 13 +- .../static/bank-template-schema.json | 569 ++++++++++++ hindsight-docs/static/openapi.json | 555 +++++++++++ .../developer/api/bank-templates.md | 437 +++++++++ skills/hindsight-docs/references/openapi.json | 555 +++++++++++ 53 files changed, 9698 insertions(+), 113 deletions(-) delete mode 100644 hindsight-api-slim/hindsight_api/alembic/versions/d6e7f8a9b0c1_drop_documents_metadata_column.py create mode 100644 hindsight-api-slim/tests/test_bank_templates.py create mode 100644 hindsight-clients/go/api_bank_templates.go create mode 100644 hindsight-clients/go/model_bank_template_config.go create mode 100644 hindsight-clients/go/model_bank_template_directive.go create mode 100644 hindsight-clients/go/model_bank_template_import_response.go create mode 100644 hindsight-clients/go/model_bank_template_manifest.go create mode 100644 hindsight-clients/go/model_bank_template_mental_model.go create mode 100644 hindsight-clients/python/hindsight_client_api/api/bank_templates_api.py create mode 100644 hindsight-clients/python/hindsight_client_api/models/bank_template_config.py create mode 100644 hindsight-clients/python/hindsight_client_api/models/bank_template_directive.py create mode 100644 hindsight-clients/python/hindsight_client_api/models/bank_template_import_response.py create mode 100644 hindsight-clients/python/hindsight_client_api/models/bank_template_manifest.py create mode 100644 hindsight-clients/python/hindsight_client_api/models/bank_template_mental_model.py create mode 100644 hindsight-control-plane/src/app/api/banks/[bankId]/export/route.ts create mode 100644 hindsight-control-plane/src/app/api/banks/[bankId]/import/route.ts create mode 100644 hindsight-docs/docs/developer/api/bank-templates.mdx create mode 100644 hindsight-docs/examples/api/bank-templates.go create mode 100644 hindsight-docs/examples/api/bank-templates.mjs create mode 100644 hindsight-docs/examples/api/bank-templates.py create mode 100644 hindsight-docs/examples/api/bank-templates.sh create mode 100644 hindsight-docs/scripts/check-templates.mjs create mode 100644 hindsight-docs/src/data/templates.json create mode 100644 hindsight-docs/src/pages/templates/index.module.css create mode 100644 hindsight-docs/src/pages/templates/index.tsx create mode 100644 hindsight-docs/static/bank-template-schema.json create mode 100644 skills/hindsight-docs/references/developer/api/bank-templates.md diff --git a/hindsight-api-slim/hindsight_api/alembic/versions/2eee35aa3cfc_case_insensitive_entities_trgm_index.py b/hindsight-api-slim/hindsight_api/alembic/versions/2eee35aa3cfc_case_insensitive_entities_trgm_index.py index 5522f660..8c58c96d 100644 --- a/hindsight-api-slim/hindsight_api/alembic/versions/2eee35aa3cfc_case_insensitive_entities_trgm_index.py +++ b/hindsight-api-slim/hindsight_api/alembic/versions/2eee35aa3cfc_case_insensitive_entities_trgm_index.py @@ -4,8 +4,8 @@ The previous GIN trigram index on canonical_name was case-sensitive, causing "Alice" and "alice" to have different trigram sets. This recreates it on LOWER(canonical_name) so the % operator matches case-insensitively. -Revision ID: 2eee35aa3cfc -Revises: d6e7f8a9b0c1 +Revision ID: d6e7f8a9b0c1 +Revises: c5d6e7f8a9b0 Create Date: 2026-03-31 """ @@ -13,8 +13,8 @@ from collections.abc import Sequence from alembic import context, op -revision: str = "2eee35aa3cfc" -down_revision: str | Sequence[str] | None = "d6e7f8a9b0c1" +revision: str = "d6e7f8a9b0c1" +down_revision: str | Sequence[str] | None = "c5d6e7f8a9b0" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None @@ -27,7 +27,7 @@ def _get_schema_prefix() -> str: def upgrade() -> None: schema = _get_schema_prefix() # Drop the old case-sensitive trigram index - op.execute(f"DROP INDEX IF EXISTS {schema}entities_canonical_name_trgm_idx") + op.execute("DROP INDEX IF EXISTS entities_canonical_name_trgm_idx") # Create case-insensitive trigram index on LOWER(canonical_name) op.execute( f"CREATE INDEX IF NOT EXISTS entities_canonical_name_lower_trgm_idx " @@ -36,8 +36,8 @@ def upgrade() -> None: def downgrade() -> None: + op.execute("DROP INDEX IF EXISTS entities_canonical_name_lower_trgm_idx") schema = _get_schema_prefix() - op.execute(f"DROP INDEX IF EXISTS {schema}entities_canonical_name_lower_trgm_idx") # Restore original case-sensitive index op.execute( f"CREATE INDEX IF NOT EXISTS entities_canonical_name_trgm_idx " diff --git a/hindsight-api-slim/hindsight_api/alembic/versions/a4b5c6d7e8f9_fix_per_bank_vector_index_type.py b/hindsight-api-slim/hindsight_api/alembic/versions/a4b5c6d7e8f9_fix_per_bank_vector_index_type.py index 0e8a0b75..e7ec79d2 100644 --- a/hindsight-api-slim/hindsight_api/alembic/versions/a4b5c6d7e8f9_fix_per_bank_vector_index_type.py +++ b/hindsight-api-slim/hindsight_api/alembic/versions/a4b5c6d7e8f9_fix_per_bank_vector_index_type.py @@ -1,7 +1,7 @@ """Fix per-bank vector indexes to match configured extension Revision ID: a4b5c6d7e8f9 -Revises: 2eee35aa3cfc +Revises: c2d3e4f5g6h7, c5d6e7f8a9b0 Create Date: 2026-04-01 Migration d5e6f7a8b9c0 hardcoded HNSW when creating per-bank partial vector @@ -21,7 +21,10 @@ from alembic import context, op from sqlalchemy import text revision: str = "a4b5c6d7e8f9" -down_revision: str | Sequence[str] | None = "2eee35aa3cfc" +# Updated: the merge migration d6e7f8a9b0c1 was renamed to d6e7f8a9b0c2 +# to avoid colliding with the case_insensitive_entities_trgm_index migration +# that shares the same revision ID. +down_revision: str | Sequence[str] | None = "d6e7f8a9b0c2" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None diff --git a/hindsight-api-slim/hindsight_api/alembic/versions/d6e7f8a9b0c1_drop_documents_metadata_column.py b/hindsight-api-slim/hindsight_api/alembic/versions/d6e7f8a9b0c1_drop_documents_metadata_column.py deleted file mode 100644 index 97d28d74..00000000 --- a/hindsight-api-slim/hindsight_api/alembic/versions/d6e7f8a9b0c1_drop_documents_metadata_column.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Drop unused metadata column from documents table - -Revision ID: d6e7f8a9b0c1 -Revises: c2d3e4f5g6h7, c5d6e7f8a9b0 -Create Date: 2026-03-30 - -The metadata column on documents was always stored as an empty dict {}. -Actual document metadata is stored inside retain_params.metadata. -""" - -from collections.abc import Sequence - -from alembic import context, op - -revision: str = "d6e7f8a9b0c1" -down_revision: str | Sequence[str] | None = ("c2d3e4f5g6h7", "c5d6e7f8a9b0") -branch_labels: str | Sequence[str] | None = None -depends_on: str | Sequence[str] | None = None - - -def _get_schema_prefix() -> str: - """Get schema prefix for table names (required for multi-tenant support).""" - schema = context.config.get_main_option("target_schema") - return f'"{schema}".' if schema else "" - - -def upgrade() -> None: - schema = _get_schema_prefix() - op.execute(f"ALTER TABLE {schema}documents DROP COLUMN IF EXISTS metadata") - - -def downgrade() -> None: - schema = _get_schema_prefix() - op.execute(f"ALTER TABLE {schema}documents ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{{}}'") diff --git a/hindsight-api-slim/hindsight_api/api/http.py b/hindsight-api-slim/hindsight_api/api/http.py index 2c1739f3..006988d5 100644 --- a/hindsight-api-slim/hindsight_api/api/http.py +++ b/hindsight-api-slim/hindsight_api/api/http.py @@ -8,12 +8,13 @@ the FastAPI application with all API endpoints. import asyncio import json import logging +import re import uuid from contextlib import asynccontextmanager from datetime import datetime, timezone from typing import Any, Literal -from fastapi import Depends, FastAPI, File, Form, Header, HTTPException, Query, UploadFile +from fastapi import Depends, FastAPI, File, Form, Header, HTTPException, Query, Request, UploadFile from hindsight_api.engine.audit import AuditEntry, AuditLogger from hindsight_api.extensions import AuthenticationError @@ -1611,6 +1612,186 @@ class UpdateMentalModelRequest(BaseModel): trigger: MentalModelTrigger | None = Field(default=None, description="Trigger settings") +# ========================================================================= +# Bank Templates (import/export) +# ========================================================================= + +# Current manifest schema version. Bump when making breaking changes. +BANK_TEMPLATE_CURRENT_VERSION = "1" + + +class BankTemplateMentalModel(BaseModel): + """A mental model definition within a bank template manifest.""" + + id: str = Field(description="Unique ID for the mental model (alphanumeric lowercase with hyphens)") + name: str = Field(description="Human-readable name for the mental model") + source_query: str = Field(description="The query to run to generate content") + tags: list[str] = FieldWithDefault(list, description="Tags for scoped visibility") + max_tokens: int = Field(default=2048, ge=256, le=8192, description="Maximum tokens for generated content") + trigger: MentalModelTrigger = FieldWithDefault(MentalModelTrigger, description="Trigger settings") + + @field_validator("id") + @classmethod + def validate_id(cls, v: str) -> str: + if not re.match(r"^[a-z0-9][a-z0-9-]*$", v): + raise ValueError( + f"Mental model id '{v}' must be alphanumeric lowercase with hyphens, starting with a letter or digit." + ) + return v + + +class BankTemplateConfig(BaseModel): + """Bank configuration fields within a template manifest. + + Only includes configurable (per-bank) fields. Credential fields + (API keys, base URLs) are intentionally excluded for security. + """ + + reflect_mission: str | None = Field(default=None, description="Mission/context for Reflect operations") + retain_mission: str | None = Field(default=None, description="Steers what gets extracted during retain") + retain_extraction_mode: str | None = Field( + default=None, description="Fact extraction mode: 'concise' (default), 'verbose', or 'custom'" + ) + retain_custom_instructions: str | None = Field( + default=None, description="Custom extraction prompt (when mode='custom')" + ) + retain_chunk_size: int | None = Field(default=None, description="Max token size for each content chunk") + enable_observations: bool | None = Field(default=None, description="Toggle observation consolidation") + observations_mission: str | None = Field(default=None, description="Controls what gets synthesised") + disposition_skepticism: int | None = Field(default=None, ge=1, le=5, description="Skepticism trait (1-5)") + disposition_literalism: int | None = Field(default=None, ge=1, le=5, description="Literalism trait (1-5)") + disposition_empathy: int | None = Field(default=None, ge=1, le=5, description="Empathy trait (1-5)") + entity_labels: list[str] | None = Field(default=None, description="Controlled vocabulary for entity labels") + entities_allow_free_form: bool | None = Field( + default=None, description="Allow entities outside the label vocabulary" + ) + + def get_config_updates(self) -> dict[str, Any]: + """Return only the fields that were explicitly set (non-None).""" + return {k: v for k, v in self.model_dump().items() if v is not None} + + +class BankTemplateDirective(BaseModel): + """A directive definition within a bank template manifest. + + Directives are matched by name on re-import: existing directives + with the same name are updated, new ones are created. + """ + + name: str = Field(description="Human-readable name for the directive (used as match key on re-import)") + content: str = Field(description="The directive text to inject into prompts") + priority: int = Field(default=0, description="Higher priority directives are injected first") + is_active: bool = Field(default=True, description="Whether this directive is active") + tags: list[str] = FieldWithDefault(list, description="Tags for filtering") + + +class BankTemplateManifest(BaseModel): + """A bank template manifest for import/export. + + Version field enables forward-compatible schema evolution: the API + auto-upgrades older manifest versions to the current schema on import. + """ + + model_config = ConfigDict( + json_schema_extra={ + "example": { + "version": "1", + "bank": { + "reflect_mission": "You are helping a support agent remember customer interactions.", + "retain_mission": "Extract customer issues, resolutions, and sentiment.", + "disposition_empathy": 5, + "enable_observations": True, + }, + "mental_models": [ + { + "id": "sentiment-overview", + "name": "Customer Sentiment Overview", + "source_query": "What is the overall sentiment trend?", + "trigger": {"refresh_after_consolidation": True}, + } + ], + "directives": [ + { + "name": "Always be empathetic", + "content": "Always respond with empathy and understanding.", + "priority": 10, + } + ], + } + } + ) + + version: str = Field(description="Manifest schema version (currently '1')") + bank: BankTemplateConfig | None = Field( + default=None, description="Bank configuration to apply. Omit to leave config unchanged." + ) + mental_models: list[BankTemplateMentalModel] | None = Field( + default=None, description="Mental models to create or update (matched by id). Omit to leave unchanged." + ) + directives: list[BankTemplateDirective] | None = Field( + default=None, description="Directives to create or update (matched by name). Omit to leave unchanged." + ) + + @field_validator("version") + @classmethod + def validate_version(cls, v: str) -> str: + try: + ver = int(v) + except ValueError: + raise ValueError(f"version must be a numeric string, got '{v}'") + if ver < 1: + raise ValueError("version must be >= 1") + if ver > int(BANK_TEMPLATE_CURRENT_VERSION): + raise ValueError( + f"version '{v}' is not supported by this server " + f"(max supported: {BANK_TEMPLATE_CURRENT_VERSION}). Please upgrade Hindsight." + ) + return v + + @field_validator("mental_models") + @classmethod + def validate_unique_mental_model_ids( + cls, + v: list[BankTemplateMentalModel] | None, + ) -> list[BankTemplateMentalModel] | None: + if v is None: + return v + ids = [m.id for m in v] + duplicates = [mid for mid in ids if ids.count(mid) > 1] + if duplicates: + raise ValueError(f"Duplicate mental model ids: {sorted(set(duplicates))}") + return v + + @field_validator("directives") + @classmethod + def validate_unique_directive_names( + cls, + v: list[BankTemplateDirective] | None, + ) -> list[BankTemplateDirective] | None: + if v is None: + return v + names = [d.name for d in v] + duplicates = [n for n in names if names.count(n) > 1] + if duplicates: + raise ValueError(f"Duplicate directive names: {sorted(set(duplicates))}") + return v + + +class BankTemplateImportResponse(BaseModel): + """Response model for the bank template import endpoint.""" + + bank_id: str = Field(description="Bank that was imported into") + config_applied: bool = Field(description="Whether bank config was updated") + mental_models_created: list[str] = FieldWithDefault(list, description="IDs of newly created mental models") + mental_models_updated: list[str] = FieldWithDefault(list, description="IDs of updated mental models") + directives_created: list[str] = FieldWithDefault(list, description="Names of newly created directives") + directives_updated: list[str] = FieldWithDefault(list, description="Names of updated directives") + operation_ids: list[str] = FieldWithDefault( + list, description="Operation IDs for mental model content generation (async)" + ) + dry_run: bool = Field(default=False, description="True if this was a validation-only run") + + class OperationResponse(BaseModel): """Response model for a single async operation.""" @@ -4176,6 +4357,304 @@ def _register_routes(app: FastAPI): logger.error(f"Error in DELETE /v1/default/banks/{bank_id}: {error_detail}") raise HTTPException(status_code=500, detail=str(e)) + # ===================================================================== + # Bank Template Import / Export + # ===================================================================== + + def _validate_template(manifest: BankTemplateManifest) -> list[str]: + """Validate a parsed manifest beyond Pydantic's structural checks. + + Returns a list of human-readable error strings (e.g. invalid + extraction mode values, conflicting settings). + """ + errors: list[str] = [] + if manifest.bank: + bank = manifest.bank + if bank.retain_extraction_mode is not None: + valid_modes = ("concise", "verbose", "custom", "chunks") + if bank.retain_extraction_mode not in valid_modes: + errors.append( + f"bank.retain_extraction_mode: must be one of {valid_modes}, " + f"got '{bank.retain_extraction_mode}'" + ) + if bank.retain_custom_instructions and bank.retain_extraction_mode != "custom": + errors.append("bank.retain_custom_instructions: requires retain_extraction_mode='custom'") + if manifest.mental_models: + for i, mm in enumerate(manifest.mental_models): + if not mm.name.strip(): + errors.append(f"mental_models[{i}].name: must not be empty") + if not mm.source_query.strip(): + errors.append(f"mental_models[{i}].source_query: must not be empty") + if manifest.directives: + for i, d in enumerate(manifest.directives): + if not d.name.strip(): + errors.append(f"directives[{i}].name: must not be empty") + if not d.content.strip(): + errors.append(f"directives[{i}].content: must not be empty") + return errors + + @app.post( + "/v1/default/banks/{bank_id}/import", + response_model=BankTemplateImportResponse, + summary="Import bank template", + description="Import a bank template manifest to create or update a bank's configuration, mental models, " + "and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. " + "Mental models are matched by id, directives by name — existing ones are updated, new ones are created. " + "Use dry_run=true to validate the manifest without applying changes.", + operation_id="import_bank_template", + tags=["Bank Templates"], + ) + @audited("import_bank_template", request_param=None) + async def api_import_bank_template( + bank_id: str, + request: Request, + dry_run: bool = Query(default=False, description="Validate only, do not apply changes"), + request_context: RequestContext = Depends(get_request_context), + ): + """Import a bank template manifest.""" + try: + # Parse raw JSON and validate against the Pydantic model manually + # so we can return clean error messages instead of raw 422s. + raw_body = await request.json() + from pydantic import ValidationError + + try: + body = BankTemplateManifest.model_validate(raw_body) + except ValidationError as e: + errors = [f"{'.'.join(str(loc) for loc in err['loc'])}: {err['msg']}" for err in e.errors()] + raise HTTPException( + status_code=400, + detail=f"Template schema validation failed: {'; '.join(errors)}", + ) + + # Semantic validation beyond Pydantic structural checks + validation_errors = _validate_template(body) + if validation_errors: + raise HTTPException( + status_code=400, + detail=f"Template validation failed: {'; '.join(validation_errors)}", + ) + if dry_run: + return BankTemplateImportResponse( + bank_id=bank_id, + config_applied=body.bank is not None, + mental_models_created=[m.id for m in (body.mental_models or [])], + directives_created=[d.name for d in (body.directives or [])], + dry_run=True, + ) + + # Ensure bank exists (auto-creates with defaults if needed) + await app.state.memory.get_bank_profile(bank_id, request_context=request_context) + + config_applied = False + if body.bank: + config_updates = body.bank.get_config_updates() + if config_updates: + await app.state.memory._config_resolver.update_bank_config(bank_id, config_updates, request_context) + config_applied = True + + created_ids: list[str] = [] + updated_ids: list[str] = [] + operation_ids: list[str] = [] + + if body.mental_models: + # Fetch existing mental models to decide create vs update + existing = await app.state.memory.list_mental_models(bank_id=bank_id, request_context=request_context) + existing_by_id = {m["id"]: m for m in existing} + + for mm in body.mental_models: + if mm.id in existing_by_id: + # Update existing mental model metadata + await app.state.memory.update_mental_model( + bank_id=bank_id, + mental_model_id=mm.id, + name=mm.name, + source_query=mm.source_query, + max_tokens=mm.max_tokens, + tags=mm.tags if mm.tags else None, + trigger=mm.trigger.model_dump() if mm.trigger else None, + request_context=request_context, + ) + # Schedule a refresh to regenerate content with updated query + result = await app.state.memory.submit_async_refresh_mental_model( + bank_id=bank_id, + mental_model_id=mm.id, + request_context=request_context, + ) + operation_ids.append(result["operation_id"]) + updated_ids.append(mm.id) + else: + # Create new mental model + mental_model = await app.state.memory.create_mental_model( + bank_id=bank_id, + name=mm.name, + source_query=mm.source_query, + content="Generating content...", + mental_model_id=mm.id, + tags=mm.tags if mm.tags else None, + max_tokens=mm.max_tokens, + trigger=mm.trigger.model_dump() if mm.trigger else None, + request_context=request_context, + ) + result = await app.state.memory.submit_async_refresh_mental_model( + bank_id=bank_id, + mental_model_id=mental_model["id"], + request_context=request_context, + ) + operation_ids.append(result["operation_id"]) + created_ids.append(mm.id) + + directives_created: list[str] = [] + directives_updated: list[str] = [] + + if body.directives: + # Fetch existing directives to decide create vs update (matched by name) + existing_directives = await app.state.memory.list_directives( + bank_id=bank_id, active_only=False, request_context=request_context + ) + existing_by_name = {d["name"]: d for d in existing_directives} + + for directive in body.directives: + if directive.name in existing_by_name: + await app.state.memory.update_directive( + bank_id=bank_id, + directive_id=existing_by_name[directive.name]["id"], + content=directive.content, + priority=directive.priority, + is_active=directive.is_active, + tags=directive.tags if directive.tags else None, + request_context=request_context, + ) + directives_updated.append(directive.name) + else: + await app.state.memory.create_directive( + bank_id=bank_id, + name=directive.name, + content=directive.content, + priority=directive.priority, + is_active=directive.is_active, + tags=directive.tags if directive.tags else None, + request_context=request_context, + ) + directives_created.append(directive.name) + + return BankTemplateImportResponse( + bank_id=bank_id, + config_applied=config_applied, + mental_models_created=created_ids, + mental_models_updated=updated_ids, + directives_created=directives_created, + directives_updated=directives_updated, + operation_ids=operation_ids, + dry_run=False, + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except OperationValidationError as e: + raise HTTPException(status_code=e.status_code, detail=e.reason) + except (AuthenticationError, HTTPException): + raise + except Exception as e: + import traceback + + error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}" + logger.error(f"Error in POST /v1/default/banks/{bank_id}/import: {error_detail}") + raise HTTPException(status_code=500, detail=str(e)) + + @app.get( + "/v1/default/banks/{bank_id}/export", + response_model=BankTemplateManifest, + summary="Export bank template", + description="Export a bank's current configuration, mental models, and directives as a template manifest. " + "The exported manifest can be imported into another bank to replicate the setup.", + operation_id="export_bank_template", + tags=["Bank Templates"], + ) + async def api_export_bank_template( + bank_id: str, + request_context: RequestContext = Depends(get_request_context), + ): + """Export a bank's config and mental models as a template manifest.""" + try: + # Authenticate and ensure bank exists + profile = await app.state.memory.get_bank_profile(bank_id, request_context=request_context) + if profile is None: + raise HTTPException(status_code=404, detail=f"Bank '{bank_id}' not found") + + # Get bank-specific config overrides (not the fully resolved config, + # so the template only contains what was explicitly set on this bank) + await app.state.memory._authenticate_tenant(request_context) + bank_overrides = await app.state.memory._config_resolver._load_bank_config(bank_id) + + # Filter to only BankTemplateConfig fields (exclude credentials, static fields) + template_config_fields = set(BankTemplateConfig.model_fields.keys()) + filtered_overrides = {k: v for k, v in bank_overrides.items() if k in template_config_fields} + bank_config = BankTemplateConfig(**filtered_overrides) if filtered_overrides else None + + # Get mental models + mental_models_raw = await app.state.memory.list_mental_models( + bank_id=bank_id, request_context=request_context + ) + template_mental_models: list[BankTemplateMentalModel] = [] + for mm in mental_models_raw: + trigger_data = mm.get("trigger", {}) + trigger = MentalModelTrigger(**trigger_data) if trigger_data else MentalModelTrigger() + template_mental_models.append( + BankTemplateMentalModel( + id=mm["id"], + name=mm["name"], + source_query=mm["source_query"], + tags=mm.get("tags", []), + max_tokens=mm.get("max_tokens", 2048), + trigger=trigger, + ) + ) + + # Get directives + directives_raw = await app.state.memory.list_directives( + bank_id=bank_id, active_only=False, request_context=request_context + ) + template_directives: list[BankTemplateDirective] = [] + for d in directives_raw: + template_directives.append( + BankTemplateDirective( + name=d["name"], + content=d["content"], + priority=d.get("priority", 0), + is_active=d.get("is_active", True), + tags=d.get("tags", []), + ) + ) + + return BankTemplateManifest( + version=BANK_TEMPLATE_CURRENT_VERSION, + bank=bank_config, + mental_models=template_mental_models if template_mental_models else None, + directives=template_directives if template_directives else None, + ) + except OperationValidationError as e: + raise HTTPException(status_code=e.status_code, detail=e.reason) + except (AuthenticationError, HTTPException): + raise + except Exception as e: + import traceback + + error_detail = f"{str(e)}\n\nTraceback:\n{traceback.format_exc()}" + logger.error(f"Error in GET /v1/default/banks/{bank_id}/export: {error_detail}") + raise HTTPException(status_code=500, detail=str(e)) + + @app.get( + "/v1/bank-template-schema", + summary="Get bank template JSON Schema", + description="Returns the JSON Schema for the bank template manifest format. " + "Use this to validate template manifests before importing.", + operation_id="get_bank_template_schema", + tags=["Bank Templates"], + ) + async def api_get_bank_template_schema(): + """Return the JSON Schema for the bank template manifest.""" + return BankTemplateManifest.model_json_schema() + @app.delete( "/v1/default/banks/{bank_id}/observations", response_model=DeleteResponse, diff --git a/hindsight-api-slim/hindsight_api/engine/memory_engine.py b/hindsight-api-slim/hindsight_api/engine/memory_engine.py index 146c61bf..3fe21e17 100644 --- a/hindsight-api-slim/hindsight_api/engine/memory_engine.py +++ b/hindsight-api-slim/hindsight_api/engine/memory_engine.py @@ -3830,7 +3830,6 @@ class MemoryEngine(MemoryEngineInterface): bank_id: str, fact_type: str | None = None, *, - delete_bank_profile: bool = True, request_context: "RequestContext", ) -> dict[str, int]: """ @@ -3917,21 +3916,20 @@ class MemoryEngine(MemoryEngineInterface): # Delete entities (cascades to unit_entities, entity_cooccurrences, memory_links with entity_id) await conn.execute(f"DELETE FROM {fq_table('entities')} WHERE bank_id = $1", bank_id) + # Delete the bank profile and retrieve internal_id for HNSW index cleanup + internal_id = await conn.fetchval( + f"DELETE FROM {fq_table('banks')} WHERE bank_id = $1 RETURNING internal_id", bank_id + ) + if internal_id: + bank_internal_id = str(internal_id) + result = { "memory_units_deleted": units_count, "entities_deleted": entities_count, "documents_deleted": documents_count, + "bank_deleted": True, } - if delete_bank_profile: - # Delete the bank profile and retrieve internal_id for HNSW index cleanup - internal_id = await conn.fetchval( - f"DELETE FROM {fq_table('banks')} WHERE bank_id = $1 RETURNING internal_id", bank_id - ) - if internal_id: - bank_internal_id = str(internal_id) - result["bank_deleted"] = True - except Exception as e: raise Exception(f"Failed to delete agent data: {str(e)}") @@ -4332,9 +4330,8 @@ class MemoryEngine(MemoryEngineInterface): link for link in links if link["from_unit_id"] in unit_id_set and link["to_unit_id"] in unit_id_set ] - # Get entity information — for visible units AND their source memories - # (observations inherit entities from source memories) - if all_relevant_ids: + # Get entity information — only for visible units + if unit_ids: unit_entities = await conn.fetch( f""" SELECT ue.unit_id, e.canonical_name @@ -4343,7 +4340,7 @@ class MemoryEngine(MemoryEngineInterface): WHERE ue.unit_id = ANY($1::uuid[]) ORDER BY ue.unit_id """, - all_relevant_ids, + unit_ids, ) else: unit_entities = [] @@ -6343,7 +6340,6 @@ class MemoryEngine(MemoryEngineInterface): *, tags: list[str] | None = None, tags_match: str = "any", - detail: str = "full", limit: int = 100, offset: int = 0, request_context: "RequestContext", @@ -6354,7 +6350,6 @@ class MemoryEngine(MemoryEngineInterface): bank_id: Bank identifier tags: Optional tags to filter by tags_match: How to match tags - 'any', 'all', or 'exact' - detail: Detail level - 'metadata', 'content', or 'full' limit: Maximum number of results offset: Offset for pagination request_context: Request context for authentication @@ -6396,14 +6391,13 @@ class MemoryEngine(MemoryEngineInterface): *params, ) - return [self._row_to_mental_model(row, detail=detail) for row in rows] + return [self._row_to_mental_model(row) for row in rows] async def get_mental_model( self, bank_id: str, mental_model_id: str, *, - detail: str = "full", request_context: "RequestContext", ) -> dict[str, Any] | None: """Get a single pinned mental model by ID. @@ -6411,7 +6405,6 @@ class MemoryEngine(MemoryEngineInterface): Args: bank_id: Bank identifier mental_model_id: Pinned mental model UUID - detail: Detail level - 'metadata', 'content', or 'full' request_context: Request context for authentication Returns: @@ -6445,7 +6438,7 @@ class MemoryEngine(MemoryEngineInterface): mental_model_id, ) - result = self._row_to_mental_model(row, detail=detail) if row else None + result = self._row_to_mental_model(row) if row else None # Post-operation hook (usage recording) if result and self._operation_validator: @@ -6843,47 +6836,34 @@ class MemoryEngine(MemoryEngineInterface): return result == "DELETE 1" - _MENTAL_MODEL_METADATA_FIELDS = frozenset({"id", "bank_id", "name", "tags", "last_refreshed_at", "created_at"}) - - def _row_to_mental_model(self, row, *, detail: str = "full") -> dict[str, Any]: - """Convert a database row to a mental model dict. - - Args: - row: Database row - detail: Detail level - 'metadata', 'content', or 'full' - """ - result: dict[str, Any] = { - "id": str(row["id"]), - "bank_id": row["bank_id"], - "name": row["name"], - "tags": row["tags"] or [], - "last_refreshed_at": row["last_refreshed_at"].isoformat() if row["last_refreshed_at"] else None, - "created_at": row["created_at"].isoformat() if row["created_at"] else None, - } - if detail == "metadata": - return result - + def _row_to_mental_model(self, row) -> dict[str, Any]: + """Convert a database row to a mental model dict.""" + reflect_response = row.get("reflect_response") + # Parse JSON string to dict if needed (asyncpg may return JSONB as string) + if isinstance(reflect_response, str): + try: + reflect_response = json.loads(reflect_response) + except json.JSONDecodeError: + reflect_response = None trigger = row.get("trigger") if isinstance(trigger, str): try: trigger = json.loads(trigger) except json.JSONDecodeError: trigger = None - result["source_query"] = row["source_query"] - result["content"] = row["content"] - result["max_tokens"] = row.get("max_tokens") - result["trigger"] = trigger - - if detail == "full": - reflect_response = row.get("reflect_response") - if isinstance(reflect_response, str): - try: - reflect_response = json.loads(reflect_response) - except json.JSONDecodeError: - reflect_response = None - result["reflect_response"] = reflect_response - - return result + return { + "id": str(row["id"]), + "bank_id": row["bank_id"], + "name": row["name"], + "source_query": row["source_query"], + "content": row["content"], + "tags": row["tags"] or [], + "max_tokens": row.get("max_tokens"), + "trigger": trigger, + "last_refreshed_at": row["last_refreshed_at"].isoformat() if row["last_refreshed_at"] else None, + "created_at": row["created_at"].isoformat() if row["created_at"] else None, + "reflect_response": reflect_response, + } # ========================================================================= # Directives - Hard rules injected into prompts diff --git a/hindsight-api-slim/tests/test_bank_templates.py b/hindsight-api-slim/tests/test_bank_templates.py new file mode 100644 index 00000000..4c13d899 --- /dev/null +++ b/hindsight-api-slim/tests/test_bank_templates.py @@ -0,0 +1,600 @@ +"""Integration tests for bank template import/export endpoints.""" + +import pytest +import pytest_asyncio +import httpx +from datetime import datetime +from hindsight_api.api import create_app + + +@pytest_asyncio.fixture +async def api_client(memory): + """Create an async test client for the FastAPI app.""" + app = create_app(memory, initialize_memory=False) + transport = httpx.ASGITransport(app=app) + async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: + yield client + + +@pytest.fixture +def bank_id(): + return f"template_test_{datetime.now().timestamp()}" + + +@pytest.fixture +def sample_template(): + return { + "version": "1", + "bank": { + "reflect_mission": "Test mission for reflect", + "retain_mission": "Extract test data carefully", + "retain_extraction_mode": "verbose", + "disposition_empathy": 5, + "disposition_skepticism": 2, + "enable_observations": True, + "observations_mission": "Track test patterns", + }, + "mental_models": [ + { + "id": "test-model-one", + "name": "Test Model One", + "source_query": "What are the key patterns?", + "tags": ["test"], + "max_tokens": 1024, + "trigger": {"refresh_after_consolidation": True}, + }, + { + "id": "test-model-two", + "name": "Test Model Two", + "source_query": "What are the common issues?", + }, + ], + "directives": [ + { + "name": "Be concise", + "content": "Always respond concisely.", + "priority": 10, + }, + { + "name": "Use examples", + "content": "Include examples when explaining concepts.", + "tags": ["style"], + }, + ], + } + + +class TestImportValidation: + """Test template manifest validation.""" + + @pytest.mark.asyncio + async def test_import_dry_run_valid(self, api_client, bank_id, sample_template): + """dry_run=true with a valid manifest returns what would happen.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import?dry_run=true", + json=sample_template, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["dry_run"] is True + assert data["config_applied"] is True + assert set(data["mental_models_created"]) == {"test-model-one", "test-model-two"} + assert set(data["directives_created"]) == {"Be concise", "Use examples"} + + @pytest.mark.asyncio + async def test_import_invalid_version(self, api_client, bank_id): + """Reject manifest with unsupported version.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={"version": "999"}, + ) + assert resp.status_code == 400 + + @pytest.mark.asyncio + async def test_import_invalid_extraction_mode(self, api_client, bank_id): + """Semantic validation catches bad extraction mode.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": {"retain_extraction_mode": "invalid_mode"}, + }, + ) + assert resp.status_code == 400 + assert "retain_extraction_mode" in resp.json()["detail"] + + @pytest.mark.asyncio + async def test_import_custom_instructions_without_custom_mode(self, api_client, bank_id): + """Validate that custom_instructions requires extraction_mode=custom.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": { + "retain_extraction_mode": "verbose", + "retain_custom_instructions": "some custom prompt", + }, + }, + ) + assert resp.status_code == 400 + assert "retain_custom_instructions" in resp.json()["detail"] + + @pytest.mark.asyncio + async def test_import_duplicate_mental_model_ids(self, api_client, bank_id): + """Reject manifest with duplicate mental model IDs.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + {"id": "dup-id", "name": "First", "source_query": "q1"}, + {"id": "dup-id", "name": "Second", "source_query": "q2"}, + ], + }, + ) + assert resp.status_code == 400 + + @pytest.mark.asyncio + async def test_import_duplicate_directive_names(self, api_client, bank_id): + """Reject manifest with duplicate directive names.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + {"name": "Same Name", "content": "First"}, + {"name": "Same Name", "content": "Second"}, + ], + }, + ) + assert resp.status_code == 400 + + @pytest.mark.asyncio + async def test_import_missing_mental_model_id(self, api_client, bank_id): + """Mental model without id is rejected.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + {"name": "No ID Model", "source_query": "test query"}, + ], + }, + ) + assert resp.status_code == 400 + + @pytest.mark.asyncio + async def test_import_invalid_mental_model_id_format(self, api_client, bank_id): + """Mental model with invalid ID format is rejected.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + {"id": "UPPERCASE-NOT-ALLOWED", "name": "Bad", "source_query": "q"}, + ], + }, + ) + assert resp.status_code == 400 + + @pytest.mark.asyncio + async def test_import_empty_manifest(self, api_client, bank_id): + """Import with no bank or mental_models is valid (no-op).""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={"version": "1"}, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is False + assert data["mental_models_created"] == [] + assert data["directives_created"] == [] + + @pytest.mark.asyncio + async def test_import_empty_mental_model_name(self, api_client, bank_id): + """Semantic validation catches empty mental model name.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + {"id": "test-mm", "name": " ", "source_query": "q"}, + ], + }, + ) + assert resp.status_code == 400 + assert "name" in resp.json()["detail"] + + @pytest.mark.asyncio + async def test_import_empty_directive_content(self, api_client, bank_id): + """Semantic validation catches empty directive content.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + {"name": "Bad Directive", "content": " "}, + ], + }, + ) + assert resp.status_code == 400 + assert "content" in resp.json()["detail"] + + +class TestImportApply: + """Test that import actually applies config, mental models, and directives.""" + + @pytest.mark.asyncio + async def test_import_applies_config(self, api_client, bank_id): + """Import with bank config applies config overrides on a new bank.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": { + "reflect_mission": "Imported mission", + "disposition_empathy": 4, + }, + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is True + assert data["dry_run"] is False + + # Verify config was actually applied + config_resp = await api_client.get(f"/v1/default/banks/{bank_id}/config") + assert config_resp.status_code == 200 + config = config_resp.json() + assert config["overrides"]["reflect_mission"] == "Imported mission" + assert config["overrides"]["disposition_empathy"] == 4 + + @pytest.mark.asyncio + async def test_import_into_existing_bank(self, api_client, bank_id): + """Import into an already-existing bank applies config and creates resources.""" + # Pre-create the bank + await api_client.put(f"/v1/default/banks/{bank_id}", json={}) + + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": {"reflect_mission": "Existing bank mission"}, + "mental_models": [ + {"id": "existing-bank-mm", "name": "MM", "source_query": "q"}, + ], + "directives": [ + {"name": "Existing Bank Directive", "content": "Be helpful"}, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is True + assert "existing-bank-mm" in data["mental_models_created"] + assert "Existing Bank Directive" in data["directives_created"] + + # Verify everything exists + config_resp = await api_client.get(f"/v1/default/banks/{bank_id}/config") + assert config_resp.json()["overrides"]["reflect_mission"] == "Existing bank mission" + + mm_resp = await api_client.get(f"/v1/default/banks/{bank_id}/mental-models/existing-bank-mm") + assert mm_resp.status_code == 200 + + dir_resp = await api_client.get(f"/v1/default/banks/{bank_id}/directives") + assert dir_resp.status_code == 200 + names = [d["name"] for d in dir_resp.json()["items"]] + assert "Existing Bank Directive" in names + + @pytest.mark.asyncio + async def test_import_creates_mental_models(self, api_client, bank_id): + """Import creates mental models and returns operation IDs.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + { + "id": "import-mm-1", + "name": "Imported Model", + "source_query": "What patterns exist?", + "tags": ["imported"], + }, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert "import-mm-1" in data["mental_models_created"] + assert len(data["operation_ids"]) == 1 + + # Verify mental model exists + mm_resp = await api_client.get(f"/v1/default/banks/{bank_id}/mental-models/import-mm-1") + assert mm_resp.status_code == 200 + mm = mm_resp.json() + assert mm["name"] == "Imported Model" + assert mm["source_query"] == "What patterns exist?" + assert mm["tags"] == ["imported"] + + @pytest.mark.asyncio + async def test_import_updates_existing_mental_models(self, api_client, bank_id): + """Re-importing updates existing mental models matched by ID.""" + # First import + await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + { + "id": "reusable-mm", + "name": "Original Name", + "source_query": "Original query", + }, + ], + }, + ) + + # Second import with same ID but different content + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + { + "id": "reusable-mm", + "name": "Updated Name", + "source_query": "Updated query", + }, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert "reusable-mm" in data["mental_models_updated"] + assert data["mental_models_created"] == [] + + # Verify update + mm_resp = await api_client.get(f"/v1/default/banks/{bank_id}/mental-models/reusable-mm") + assert mm_resp.status_code == 200 + mm = mm_resp.json() + assert mm["name"] == "Updated Name" + assert mm["source_query"] == "Updated query" + + @pytest.mark.asyncio + async def test_import_creates_directives(self, api_client, bank_id): + """Import creates directives.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + { + "name": "Test Directive", + "content": "Always be helpful and precise.", + "priority": 5, + "tags": ["test"], + }, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert "Test Directive" in data["directives_created"] + assert data["directives_updated"] == [] + + # Verify directive exists + dir_resp = await api_client.get(f"/v1/default/banks/{bank_id}/directives") + assert dir_resp.status_code == 200 + items = dir_resp.json()["items"] + assert len(items) == 1 + assert items[0]["name"] == "Test Directive" + assert items[0]["content"] == "Always be helpful and precise." + assert items[0]["priority"] == 5 + assert items[0]["tags"] == ["test"] + + @pytest.mark.asyncio + async def test_import_updates_existing_directives(self, api_client, bank_id): + """Re-importing updates existing directives matched by name.""" + # First import + await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + {"name": "Reusable Directive", "content": "Original content", "priority": 1}, + ], + }, + ) + + # Second import with same name but different content + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + {"name": "Reusable Directive", "content": "Updated content", "priority": 10}, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert "Reusable Directive" in data["directives_updated"] + assert data["directives_created"] == [] + + # Verify update + dir_resp = await api_client.get(f"/v1/default/banks/{bank_id}/directives") + items = dir_resp.json()["items"] + directive = [d for d in items if d["name"] == "Reusable Directive"][0] + assert directive["content"] == "Updated content" + assert directive["priority"] == 10 + + @pytest.mark.asyncio + async def test_import_config_only(self, api_client, bank_id): + """Import with only bank config (no mental_models or directives) works.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": {"retain_extraction_mode": "verbose"}, + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is True + assert data["mental_models_created"] == [] + assert data["directives_created"] == [] + assert data["operation_ids"] == [] + + @pytest.mark.asyncio + async def test_import_mental_models_only(self, api_client, bank_id): + """Import with only mental_models works.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "mental_models": [ + {"id": "mm-only", "name": "MM Only", "source_query": "test"}, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is False + assert "mm-only" in data["mental_models_created"] + assert data["directives_created"] == [] + + @pytest.mark.asyncio + async def test_import_directives_only(self, api_client, bank_id): + """Import with only directives works.""" + resp = await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "directives": [ + {"name": "Dir Only", "content": "test directive"}, + ], + }, + ) + assert resp.status_code == 200 + data = resp.json() + assert data["config_applied"] is False + assert data["mental_models_created"] == [] + assert "Dir Only" in data["directives_created"] + + +class TestExport: + """Test bank template export.""" + + @pytest.mark.asyncio + async def test_export_empty_bank(self, api_client, bank_id): + """Export a bank with no overrides returns minimal manifest.""" + # Create bank + await api_client.put(f"/v1/default/banks/{bank_id}", json={}) + + resp = await api_client.get(f"/v1/default/banks/{bank_id}/export") + assert resp.status_code == 200 + data = resp.json() + assert data["version"] == "1" + assert data["bank"] is None + assert data["mental_models"] is None + assert data["directives"] is None + + @pytest.mark.asyncio + async def test_export_after_import(self, api_client, bank_id): + """Export after import returns the imported config, mental models, and directives.""" + template = { + "version": "1", + "bank": { + "reflect_mission": "Roundtrip mission", + "disposition_empathy": 3, + }, + "mental_models": [ + { + "id": "roundtrip-mm", + "name": "Roundtrip Model", + "source_query": "What happened?", + "tags": ["roundtrip"], + "max_tokens": 512, + }, + ], + "directives": [ + { + "name": "Roundtrip Directive", + "content": "Be thorough.", + "priority": 3, + "tags": ["roundtrip"], + }, + ], + } + + # Import + import_resp = await api_client.post(f"/v1/default/banks/{bank_id}/import", json=template) + assert import_resp.status_code == 200 + + # Export + resp = await api_client.get(f"/v1/default/banks/{bank_id}/export") + assert resp.status_code == 200 + data = resp.json() + + assert data["version"] == "1" + assert data["bank"]["reflect_mission"] == "Roundtrip mission" + assert data["bank"]["disposition_empathy"] == 3 + + assert len(data["mental_models"]) == 1 + mm = data["mental_models"][0] + assert mm["id"] == "roundtrip-mm" + assert mm["name"] == "Roundtrip Model" + assert mm["source_query"] == "What happened?" + assert mm["tags"] == ["roundtrip"] + assert mm["max_tokens"] == 512 + + assert len(data["directives"]) == 1 + d = data["directives"][0] + assert d["name"] == "Roundtrip Directive" + assert d["content"] == "Be thorough." + assert d["priority"] == 3 + assert d["tags"] == ["roundtrip"] + + @pytest.mark.asyncio + async def test_export_reimport_roundtrip(self, api_client, bank_id): + """Exported manifest can be re-imported into a new bank.""" + # Set up source bank + await api_client.post( + f"/v1/default/banks/{bank_id}/import", + json={ + "version": "1", + "bank": {"retain_mission": "Roundtrip test"}, + "mental_models": [ + {"id": "rt-mm", "name": "RT Model", "source_query": "test query"}, + ], + "directives": [ + {"name": "RT Directive", "content": "test directive"}, + ], + }, + ) + + # Export + export_resp = await api_client.get(f"/v1/default/banks/{bank_id}/export") + assert export_resp.status_code == 200 + exported = export_resp.json() + + # Import into a new bank + new_bank_id = f"{bank_id}_clone" + import_resp = await api_client.post( + f"/v1/default/banks/{new_bank_id}/import", + json=exported, + ) + assert import_resp.status_code == 200 + data = import_resp.json() + assert data["config_applied"] is True + assert "rt-mm" in data["mental_models_created"] + assert "RT Directive" in data["directives_created"] + + @pytest.mark.asyncio + async def test_export_nonexistent_bank(self, api_client): + """Export from a nonexistent bank returns the bank with defaults (auto-created).""" + resp = await api_client.get("/v1/default/banks/nonexistent-export-test/export") + # get_bank_profile auto-creates, so this returns a valid empty manifest + assert resp.status_code == 200 + data = resp.json() + assert data["version"] == "1" diff --git a/hindsight-clients/go/api/openapi.yaml b/hindsight-clients/go/api/openapi.yaml index 455e3d22..bc7db3aa 100644 --- a/hindsight-clients/go/api/openapi.yaml +++ b/hindsight-clients/go/api/openapi.yaml @@ -2102,6 +2102,111 @@ paths: summary: Create or update memory bank tags: - Banks + /v1/default/banks/{bank_id}/import: + post: + description: "Import a bank template manifest to create or update a bank's configuration,\ + \ mental models, and directives. If the bank does not exist it is created.\ + \ Config fields are applied as per-bank overrides. Mental models are matched\ + \ by id, directives by name — existing ones are updated, new ones are created.\ + \ Use dry_run=true to validate the manifest without applying changes." + operationId: import_bank_template + parameters: + - explode: false + in: path + name: bank_id + required: true + schema: + title: Bank Id + type: string + style: simple + - description: "Validate only, do not apply changes" + explode: true + in: query + name: dry_run + required: false + schema: + default: false + description: "Validate only, do not apply changes" + title: Dry Run + type: boolean + style: form + - explode: false + in: header + name: authorization + required: false + schema: + nullable: true + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/BankTemplateImportResponse' + description: Successful Response + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + summary: Import bank template + tags: + - Bank Templates + /v1/default/banks/{bank_id}/export: + get: + description: "Export a bank's current configuration, mental models, and directives\ + \ as a template manifest. The exported manifest can be imported into another\ + \ bank to replicate the setup." + operationId: export_bank_template + parameters: + - explode: false + in: path + name: bank_id + required: true + schema: + title: Bank Id + type: string + style: simple + - explode: false + in: header + name: authorization + required: false + schema: + nullable: true + type: string + style: simple + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/BankTemplateManifest' + description: Successful Response + "422": + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' + description: Validation Error + summary: Export bank template + tags: + - Bank Templates + /v1/bank-template-schema: + get: + description: Returns the JSON Schema for the bank template manifest format. + Use this to validate template manifests before importing. + operationId: get_bank_template_schema + responses: + "200": + content: + application/json: + schema: {} + description: Successful Response + summary: Get bank template JSON Schema + tags: + - Bank Templates /v1/default/banks/{bank_id}/observations: delete: description: Delete all observations for a memory bank. This is useful for resetting @@ -3420,6 +3525,242 @@ components: - total_links - total_nodes title: BankStatsResponse + BankTemplateConfig: + description: |- + Bank configuration fields within a template manifest. + + Only includes configurable (per-bank) fields. Credential fields + (API keys, base URLs) are intentionally excluded for security. + properties: + reflect_mission: + nullable: true + type: string + retain_mission: + nullable: true + type: string + retain_extraction_mode: + nullable: true + type: string + retain_custom_instructions: + nullable: true + type: string + retain_chunk_size: + nullable: true + type: integer + enable_observations: + nullable: true + type: boolean + observations_mission: + nullable: true + type: string + disposition_skepticism: + maximum: 5.0 + minimum: 1.0 + nullable: true + type: integer + disposition_literalism: + maximum: 5.0 + minimum: 1.0 + nullable: true + type: integer + disposition_empathy: + maximum: 5.0 + minimum: 1.0 + nullable: true + type: integer + entity_labels: + items: + type: string + nullable: true + type: array + entities_allow_free_form: + nullable: true + type: boolean + title: BankTemplateConfig + BankTemplateDirective: + description: |- + A directive definition within a bank template manifest. + + Directives are matched by name on re-import: existing directives + with the same name are updated, new ones are created. + properties: + name: + description: Human-readable name for the directive (used as match key on + re-import) + title: Name + type: string + content: + description: The directive text to inject into prompts + title: Content + type: string + priority: + default: 0 + description: Higher priority directives are injected first + title: Priority + type: integer + is_active: + default: true + description: Whether this directive is active + title: Is Active + type: boolean + tags: + default: [] + description: Tags for filtering + items: + type: string + type: array + required: + - content + - name + title: BankTemplateDirective + BankTemplateImportResponse: + description: Response model for the bank template import endpoint. + example: + operation_ids: + - operation_ids + - operation_ids + directives_created: + - directives_created + - directives_created + bank_id: bank_id + mental_models_updated: + - mental_models_updated + - mental_models_updated + directives_updated: + - directives_updated + - directives_updated + config_applied: true + mental_models_created: + - mental_models_created + - mental_models_created + dry_run: false + properties: + bank_id: + description: Bank that was imported into + title: Bank Id + type: string + config_applied: + description: Whether bank config was updated + title: Config Applied + type: boolean + mental_models_created: + default: [] + description: IDs of newly created mental models + items: + type: string + type: array + mental_models_updated: + default: [] + description: IDs of updated mental models + items: + type: string + type: array + directives_created: + default: [] + description: Names of newly created directives + items: + type: string + type: array + directives_updated: + default: [] + description: Names of updated directives + items: + type: string + type: array + operation_ids: + default: [] + description: Operation IDs for mental model content generation (async) + items: + type: string + type: array + dry_run: + default: false + description: True if this was a validation-only run + title: Dry Run + type: boolean + required: + - bank_id + - config_applied + title: BankTemplateImportResponse + BankTemplateManifest: + description: |- + A bank template manifest for import/export. + + Version field enables forward-compatible schema evolution: the API + auto-upgrades older manifest versions to the current schema on import. + example: + bank: + disposition_empathy: 5 + enable_observations: true + reflect_mission: You are helping a support agent remember customer interactions. + retain_mission: "Extract customer issues, resolutions, and sentiment." + directives: + - content: Always respond with empathy and understanding. + name: Always be empathetic + priority: 10 + mental_models: + - id: sentiment-overview + name: Customer Sentiment Overview + source_query: What is the overall sentiment trend? + trigger: + refresh_after_consolidation: true + version: "1" + properties: + version: + description: Manifest schema version (currently '1') + title: Version + type: string + bank: + $ref: '#/components/schemas/BankTemplateConfig' + mental_models: + items: + $ref: '#/components/schemas/BankTemplateMentalModel' + nullable: true + type: array + directives: + items: + $ref: '#/components/schemas/BankTemplateDirective' + nullable: true + type: array + required: + - version + title: BankTemplateManifest + BankTemplateMentalModel: + description: A mental model definition within a bank template manifest. + properties: + id: + description: Unique ID for the mental model (alphanumeric lowercase with + hyphens) + title: Id + type: string + name: + description: Human-readable name for the mental model + title: Name + type: string + source_query: + description: The query to run to generate content + title: Source Query + type: string + tags: + default: [] + description: Tags for scoped visibility + items: + type: string + type: array + max_tokens: + default: 2048 + description: Maximum tokens for generated content + maximum: 8192.0 + minimum: 256.0 + title: Max Tokens + type: integer + trigger: + $ref: '#/components/schemas/MentalModelTrigger-Output' + required: + - id + - name + - source_query + title: BankTemplateMentalModel Body_file_retain: properties: files: diff --git a/hindsight-clients/go/api_bank_templates.go b/hindsight-clients/go/api_bank_templates.go new file mode 100644 index 00000000..94af2c79 --- /dev/null +++ b/hindsight-clients/go/api_bank_templates.go @@ -0,0 +1,380 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + "strings" +) + + +// BankTemplatesAPIService BankTemplatesAPI service +type BankTemplatesAPIService service + +type ApiExportBankTemplateRequest struct { + ctx context.Context + ApiService *BankTemplatesAPIService + bankId string + authorization *string +} + +func (r ApiExportBankTemplateRequest) Authorization(authorization string) ApiExportBankTemplateRequest { + r.authorization = &authorization + return r +} + +func (r ApiExportBankTemplateRequest) Execute() (*BankTemplateManifest, *http.Response, error) { + return r.ApiService.ExportBankTemplateExecute(r) +} + +/* +ExportBankTemplate Export bank template + +Export a bank's current configuration, mental models, and directives as a template manifest. The exported manifest can be imported into another bank to replicate the setup. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param bankId + @return ApiExportBankTemplateRequest +*/ +func (a *BankTemplatesAPIService) ExportBankTemplate(ctx context.Context, bankId string) ApiExportBankTemplateRequest { + return ApiExportBankTemplateRequest{ + ApiService: a, + ctx: ctx, + bankId: bankId, + } +} + +// Execute executes the request +// @return BankTemplateManifest +func (a *BankTemplatesAPIService) ExportBankTemplateExecute(r ApiExportBankTemplateRequest) (*BankTemplateManifest, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *BankTemplateManifest + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BankTemplatesAPIService.ExportBankTemplate") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/v1/default/banks/{bank_id}/export" + localVarPath = strings.Replace(localVarPath, "{"+"bank_id"+"}", url.PathEscape(parameterValueToString(r.bankId, "bankId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.authorization != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "authorization", r.authorization, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 422 { + var v HTTPValidationError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiGetBankTemplateSchemaRequest struct { + ctx context.Context + ApiService *BankTemplatesAPIService +} + +func (r ApiGetBankTemplateSchemaRequest) Execute() (interface{}, *http.Response, error) { + return r.ApiService.GetBankTemplateSchemaExecute(r) +} + +/* +GetBankTemplateSchema Get bank template JSON Schema + +Returns the JSON Schema for the bank template manifest format. Use this to validate template manifests before importing. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiGetBankTemplateSchemaRequest +*/ +func (a *BankTemplatesAPIService) GetBankTemplateSchema(ctx context.Context) ApiGetBankTemplateSchemaRequest { + return ApiGetBankTemplateSchemaRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return interface{} +func (a *BankTemplatesAPIService) GetBankTemplateSchemaExecute(r ApiGetBankTemplateSchemaRequest) (interface{}, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue interface{} + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BankTemplatesAPIService.GetBankTemplateSchema") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/v1/bank-template-schema" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiImportBankTemplateRequest struct { + ctx context.Context + ApiService *BankTemplatesAPIService + bankId string + dryRun *bool + authorization *string +} + +// Validate only, do not apply changes +func (r ApiImportBankTemplateRequest) DryRun(dryRun bool) ApiImportBankTemplateRequest { + r.dryRun = &dryRun + return r +} + +func (r ApiImportBankTemplateRequest) Authorization(authorization string) ApiImportBankTemplateRequest { + r.authorization = &authorization + return r +} + +func (r ApiImportBankTemplateRequest) Execute() (*BankTemplateImportResponse, *http.Response, error) { + return r.ApiService.ImportBankTemplateExecute(r) +} + +/* +ImportBankTemplate Import bank template + +Import a bank template manifest to create or update a bank's configuration, mental models, and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. Mental models are matched by id, directives by name — existing ones are updated, new ones are created. Use dry_run=true to validate the manifest without applying changes. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param bankId + @return ApiImportBankTemplateRequest +*/ +func (a *BankTemplatesAPIService) ImportBankTemplate(ctx context.Context, bankId string) ApiImportBankTemplateRequest { + return ApiImportBankTemplateRequest{ + ApiService: a, + ctx: ctx, + bankId: bankId, + } +} + +// Execute executes the request +// @return BankTemplateImportResponse +func (a *BankTemplatesAPIService) ImportBankTemplateExecute(r ApiImportBankTemplateRequest) (*BankTemplateImportResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *BankTemplateImportResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BankTemplatesAPIService.ImportBankTemplate") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/v1/default/banks/{bank_id}/import" + localVarPath = strings.Replace(localVarPath, "{"+"bank_id"+"}", url.PathEscape(parameterValueToString(r.bankId, "bankId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.dryRun != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "dry_run", r.dryRun, "form", "") + } else { + var defaultValue bool = false + r.dryRun = &defaultValue + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.authorization != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "authorization", r.authorization, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 422 { + var v HTTPValidationError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/hindsight-clients/go/client.go b/hindsight-clients/go/client.go index 88f77cfb..75bf7b54 100644 --- a/hindsight-clients/go/client.go +++ b/hindsight-clients/go/client.go @@ -51,6 +51,8 @@ type APIClient struct { AuditAPI *AuditAPIService + BankTemplatesAPI *BankTemplatesAPIService + BanksAPI *BanksAPIService DirectivesAPI *DirectivesAPIService @@ -89,6 +91,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { // API Services c.AuditAPI = (*AuditAPIService)(&c.common) + c.BankTemplatesAPI = (*BankTemplatesAPIService)(&c.common) c.BanksAPI = (*BanksAPIService)(&c.common) c.DirectivesAPI = (*DirectivesAPIService)(&c.common) c.DocumentsAPI = (*DocumentsAPIService)(&c.common) diff --git a/hindsight-clients/go/model_bank_template_config.go b/hindsight-clients/go/model_bank_template_config.go new file mode 100644 index 00000000..080c548f --- /dev/null +++ b/hindsight-clients/go/model_bank_template_config.go @@ -0,0 +1,633 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "encoding/json" +) + +// checks if the BankTemplateConfig type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BankTemplateConfig{} + +// BankTemplateConfig Bank configuration fields within a template manifest. Only includes configurable (per-bank) fields. Credential fields (API keys, base URLs) are intentionally excluded for security. +type BankTemplateConfig struct { + ReflectMission NullableString `json:"reflect_mission,omitempty"` + RetainMission NullableString `json:"retain_mission,omitempty"` + RetainExtractionMode NullableString `json:"retain_extraction_mode,omitempty"` + RetainCustomInstructions NullableString `json:"retain_custom_instructions,omitempty"` + RetainChunkSize NullableInt32 `json:"retain_chunk_size,omitempty"` + EnableObservations NullableBool `json:"enable_observations,omitempty"` + ObservationsMission NullableString `json:"observations_mission,omitempty"` + DispositionSkepticism NullableInt32 `json:"disposition_skepticism,omitempty"` + DispositionLiteralism NullableInt32 `json:"disposition_literalism,omitempty"` + DispositionEmpathy NullableInt32 `json:"disposition_empathy,omitempty"` + EntityLabels []string `json:"entity_labels,omitempty"` + EntitiesAllowFreeForm NullableBool `json:"entities_allow_free_form,omitempty"` +} + +// NewBankTemplateConfig instantiates a new BankTemplateConfig object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBankTemplateConfig() *BankTemplateConfig { + this := BankTemplateConfig{} + return &this +} + +// NewBankTemplateConfigWithDefaults instantiates a new BankTemplateConfig object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBankTemplateConfigWithDefaults() *BankTemplateConfig { + this := BankTemplateConfig{} + return &this +} + +// GetReflectMission returns the ReflectMission field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetReflectMission() string { + if o == nil || IsNil(o.ReflectMission.Get()) { + var ret string + return ret + } + return *o.ReflectMission.Get() +} + +// GetReflectMissionOk returns a tuple with the ReflectMission field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetReflectMissionOk() (*string, bool) { + if o == nil { + return nil, false + } + return o.ReflectMission.Get(), o.ReflectMission.IsSet() +} + +// HasReflectMission returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasReflectMission() bool { + if o != nil && o.ReflectMission.IsSet() { + return true + } + + return false +} + +// SetReflectMission gets a reference to the given NullableString and assigns it to the ReflectMission field. +func (o *BankTemplateConfig) SetReflectMission(v string) { + o.ReflectMission.Set(&v) +} +// SetReflectMissionNil sets the value for ReflectMission to be an explicit nil +func (o *BankTemplateConfig) SetReflectMissionNil() { + o.ReflectMission.Set(nil) +} + +// UnsetReflectMission ensures that no value is present for ReflectMission, not even an explicit nil +func (o *BankTemplateConfig) UnsetReflectMission() { + o.ReflectMission.Unset() +} + +// GetRetainMission returns the RetainMission field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetRetainMission() string { + if o == nil || IsNil(o.RetainMission.Get()) { + var ret string + return ret + } + return *o.RetainMission.Get() +} + +// GetRetainMissionOk returns a tuple with the RetainMission field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetRetainMissionOk() (*string, bool) { + if o == nil { + return nil, false + } + return o.RetainMission.Get(), o.RetainMission.IsSet() +} + +// HasRetainMission returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasRetainMission() bool { + if o != nil && o.RetainMission.IsSet() { + return true + } + + return false +} + +// SetRetainMission gets a reference to the given NullableString and assigns it to the RetainMission field. +func (o *BankTemplateConfig) SetRetainMission(v string) { + o.RetainMission.Set(&v) +} +// SetRetainMissionNil sets the value for RetainMission to be an explicit nil +func (o *BankTemplateConfig) SetRetainMissionNil() { + o.RetainMission.Set(nil) +} + +// UnsetRetainMission ensures that no value is present for RetainMission, not even an explicit nil +func (o *BankTemplateConfig) UnsetRetainMission() { + o.RetainMission.Unset() +} + +// GetRetainExtractionMode returns the RetainExtractionMode field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetRetainExtractionMode() string { + if o == nil || IsNil(o.RetainExtractionMode.Get()) { + var ret string + return ret + } + return *o.RetainExtractionMode.Get() +} + +// GetRetainExtractionModeOk returns a tuple with the RetainExtractionMode field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetRetainExtractionModeOk() (*string, bool) { + if o == nil { + return nil, false + } + return o.RetainExtractionMode.Get(), o.RetainExtractionMode.IsSet() +} + +// HasRetainExtractionMode returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasRetainExtractionMode() bool { + if o != nil && o.RetainExtractionMode.IsSet() { + return true + } + + return false +} + +// SetRetainExtractionMode gets a reference to the given NullableString and assigns it to the RetainExtractionMode field. +func (o *BankTemplateConfig) SetRetainExtractionMode(v string) { + o.RetainExtractionMode.Set(&v) +} +// SetRetainExtractionModeNil sets the value for RetainExtractionMode to be an explicit nil +func (o *BankTemplateConfig) SetRetainExtractionModeNil() { + o.RetainExtractionMode.Set(nil) +} + +// UnsetRetainExtractionMode ensures that no value is present for RetainExtractionMode, not even an explicit nil +func (o *BankTemplateConfig) UnsetRetainExtractionMode() { + o.RetainExtractionMode.Unset() +} + +// GetRetainCustomInstructions returns the RetainCustomInstructions field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetRetainCustomInstructions() string { + if o == nil || IsNil(o.RetainCustomInstructions.Get()) { + var ret string + return ret + } + return *o.RetainCustomInstructions.Get() +} + +// GetRetainCustomInstructionsOk returns a tuple with the RetainCustomInstructions field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetRetainCustomInstructionsOk() (*string, bool) { + if o == nil { + return nil, false + } + return o.RetainCustomInstructions.Get(), o.RetainCustomInstructions.IsSet() +} + +// HasRetainCustomInstructions returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasRetainCustomInstructions() bool { + if o != nil && o.RetainCustomInstructions.IsSet() { + return true + } + + return false +} + +// SetRetainCustomInstructions gets a reference to the given NullableString and assigns it to the RetainCustomInstructions field. +func (o *BankTemplateConfig) SetRetainCustomInstructions(v string) { + o.RetainCustomInstructions.Set(&v) +} +// SetRetainCustomInstructionsNil sets the value for RetainCustomInstructions to be an explicit nil +func (o *BankTemplateConfig) SetRetainCustomInstructionsNil() { + o.RetainCustomInstructions.Set(nil) +} + +// UnsetRetainCustomInstructions ensures that no value is present for RetainCustomInstructions, not even an explicit nil +func (o *BankTemplateConfig) UnsetRetainCustomInstructions() { + o.RetainCustomInstructions.Unset() +} + +// GetRetainChunkSize returns the RetainChunkSize field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetRetainChunkSize() int32 { + if o == nil || IsNil(o.RetainChunkSize.Get()) { + var ret int32 + return ret + } + return *o.RetainChunkSize.Get() +} + +// GetRetainChunkSizeOk returns a tuple with the RetainChunkSize field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetRetainChunkSizeOk() (*int32, bool) { + if o == nil { + return nil, false + } + return o.RetainChunkSize.Get(), o.RetainChunkSize.IsSet() +} + +// HasRetainChunkSize returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasRetainChunkSize() bool { + if o != nil && o.RetainChunkSize.IsSet() { + return true + } + + return false +} + +// SetRetainChunkSize gets a reference to the given NullableInt32 and assigns it to the RetainChunkSize field. +func (o *BankTemplateConfig) SetRetainChunkSize(v int32) { + o.RetainChunkSize.Set(&v) +} +// SetRetainChunkSizeNil sets the value for RetainChunkSize to be an explicit nil +func (o *BankTemplateConfig) SetRetainChunkSizeNil() { + o.RetainChunkSize.Set(nil) +} + +// UnsetRetainChunkSize ensures that no value is present for RetainChunkSize, not even an explicit nil +func (o *BankTemplateConfig) UnsetRetainChunkSize() { + o.RetainChunkSize.Unset() +} + +// GetEnableObservations returns the EnableObservations field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetEnableObservations() bool { + if o == nil || IsNil(o.EnableObservations.Get()) { + var ret bool + return ret + } + return *o.EnableObservations.Get() +} + +// GetEnableObservationsOk returns a tuple with the EnableObservations field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetEnableObservationsOk() (*bool, bool) { + if o == nil { + return nil, false + } + return o.EnableObservations.Get(), o.EnableObservations.IsSet() +} + +// HasEnableObservations returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasEnableObservations() bool { + if o != nil && o.EnableObservations.IsSet() { + return true + } + + return false +} + +// SetEnableObservations gets a reference to the given NullableBool and assigns it to the EnableObservations field. +func (o *BankTemplateConfig) SetEnableObservations(v bool) { + o.EnableObservations.Set(&v) +} +// SetEnableObservationsNil sets the value for EnableObservations to be an explicit nil +func (o *BankTemplateConfig) SetEnableObservationsNil() { + o.EnableObservations.Set(nil) +} + +// UnsetEnableObservations ensures that no value is present for EnableObservations, not even an explicit nil +func (o *BankTemplateConfig) UnsetEnableObservations() { + o.EnableObservations.Unset() +} + +// GetObservationsMission returns the ObservationsMission field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetObservationsMission() string { + if o == nil || IsNil(o.ObservationsMission.Get()) { + var ret string + return ret + } + return *o.ObservationsMission.Get() +} + +// GetObservationsMissionOk returns a tuple with the ObservationsMission field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetObservationsMissionOk() (*string, bool) { + if o == nil { + return nil, false + } + return o.ObservationsMission.Get(), o.ObservationsMission.IsSet() +} + +// HasObservationsMission returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasObservationsMission() bool { + if o != nil && o.ObservationsMission.IsSet() { + return true + } + + return false +} + +// SetObservationsMission gets a reference to the given NullableString and assigns it to the ObservationsMission field. +func (o *BankTemplateConfig) SetObservationsMission(v string) { + o.ObservationsMission.Set(&v) +} +// SetObservationsMissionNil sets the value for ObservationsMission to be an explicit nil +func (o *BankTemplateConfig) SetObservationsMissionNil() { + o.ObservationsMission.Set(nil) +} + +// UnsetObservationsMission ensures that no value is present for ObservationsMission, not even an explicit nil +func (o *BankTemplateConfig) UnsetObservationsMission() { + o.ObservationsMission.Unset() +} + +// GetDispositionSkepticism returns the DispositionSkepticism field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetDispositionSkepticism() int32 { + if o == nil || IsNil(o.DispositionSkepticism.Get()) { + var ret int32 + return ret + } + return *o.DispositionSkepticism.Get() +} + +// GetDispositionSkepticismOk returns a tuple with the DispositionSkepticism field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetDispositionSkepticismOk() (*int32, bool) { + if o == nil { + return nil, false + } + return o.DispositionSkepticism.Get(), o.DispositionSkepticism.IsSet() +} + +// HasDispositionSkepticism returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasDispositionSkepticism() bool { + if o != nil && o.DispositionSkepticism.IsSet() { + return true + } + + return false +} + +// SetDispositionSkepticism gets a reference to the given NullableInt32 and assigns it to the DispositionSkepticism field. +func (o *BankTemplateConfig) SetDispositionSkepticism(v int32) { + o.DispositionSkepticism.Set(&v) +} +// SetDispositionSkepticismNil sets the value for DispositionSkepticism to be an explicit nil +func (o *BankTemplateConfig) SetDispositionSkepticismNil() { + o.DispositionSkepticism.Set(nil) +} + +// UnsetDispositionSkepticism ensures that no value is present for DispositionSkepticism, not even an explicit nil +func (o *BankTemplateConfig) UnsetDispositionSkepticism() { + o.DispositionSkepticism.Unset() +} + +// GetDispositionLiteralism returns the DispositionLiteralism field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetDispositionLiteralism() int32 { + if o == nil || IsNil(o.DispositionLiteralism.Get()) { + var ret int32 + return ret + } + return *o.DispositionLiteralism.Get() +} + +// GetDispositionLiteralismOk returns a tuple with the DispositionLiteralism field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetDispositionLiteralismOk() (*int32, bool) { + if o == nil { + return nil, false + } + return o.DispositionLiteralism.Get(), o.DispositionLiteralism.IsSet() +} + +// HasDispositionLiteralism returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasDispositionLiteralism() bool { + if o != nil && o.DispositionLiteralism.IsSet() { + return true + } + + return false +} + +// SetDispositionLiteralism gets a reference to the given NullableInt32 and assigns it to the DispositionLiteralism field. +func (o *BankTemplateConfig) SetDispositionLiteralism(v int32) { + o.DispositionLiteralism.Set(&v) +} +// SetDispositionLiteralismNil sets the value for DispositionLiteralism to be an explicit nil +func (o *BankTemplateConfig) SetDispositionLiteralismNil() { + o.DispositionLiteralism.Set(nil) +} + +// UnsetDispositionLiteralism ensures that no value is present for DispositionLiteralism, not even an explicit nil +func (o *BankTemplateConfig) UnsetDispositionLiteralism() { + o.DispositionLiteralism.Unset() +} + +// GetDispositionEmpathy returns the DispositionEmpathy field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetDispositionEmpathy() int32 { + if o == nil || IsNil(o.DispositionEmpathy.Get()) { + var ret int32 + return ret + } + return *o.DispositionEmpathy.Get() +} + +// GetDispositionEmpathyOk returns a tuple with the DispositionEmpathy field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetDispositionEmpathyOk() (*int32, bool) { + if o == nil { + return nil, false + } + return o.DispositionEmpathy.Get(), o.DispositionEmpathy.IsSet() +} + +// HasDispositionEmpathy returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasDispositionEmpathy() bool { + if o != nil && o.DispositionEmpathy.IsSet() { + return true + } + + return false +} + +// SetDispositionEmpathy gets a reference to the given NullableInt32 and assigns it to the DispositionEmpathy field. +func (o *BankTemplateConfig) SetDispositionEmpathy(v int32) { + o.DispositionEmpathy.Set(&v) +} +// SetDispositionEmpathyNil sets the value for DispositionEmpathy to be an explicit nil +func (o *BankTemplateConfig) SetDispositionEmpathyNil() { + o.DispositionEmpathy.Set(nil) +} + +// UnsetDispositionEmpathy ensures that no value is present for DispositionEmpathy, not even an explicit nil +func (o *BankTemplateConfig) UnsetDispositionEmpathy() { + o.DispositionEmpathy.Unset() +} + +// GetEntityLabels returns the EntityLabels field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetEntityLabels() []string { + if o == nil { + var ret []string + return ret + } + return o.EntityLabels +} + +// GetEntityLabelsOk returns a tuple with the EntityLabels field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetEntityLabelsOk() ([]string, bool) { + if o == nil || IsNil(o.EntityLabels) { + return nil, false + } + return o.EntityLabels, true +} + +// HasEntityLabels returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasEntityLabels() bool { + if o != nil && !IsNil(o.EntityLabels) { + return true + } + + return false +} + +// SetEntityLabels gets a reference to the given []string and assigns it to the EntityLabels field. +func (o *BankTemplateConfig) SetEntityLabels(v []string) { + o.EntityLabels = v +} + +// GetEntitiesAllowFreeForm returns the EntitiesAllowFreeForm field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateConfig) GetEntitiesAllowFreeForm() bool { + if o == nil || IsNil(o.EntitiesAllowFreeForm.Get()) { + var ret bool + return ret + } + return *o.EntitiesAllowFreeForm.Get() +} + +// GetEntitiesAllowFreeFormOk returns a tuple with the EntitiesAllowFreeForm field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateConfig) GetEntitiesAllowFreeFormOk() (*bool, bool) { + if o == nil { + return nil, false + } + return o.EntitiesAllowFreeForm.Get(), o.EntitiesAllowFreeForm.IsSet() +} + +// HasEntitiesAllowFreeForm returns a boolean if a field has been set. +func (o *BankTemplateConfig) HasEntitiesAllowFreeForm() bool { + if o != nil && o.EntitiesAllowFreeForm.IsSet() { + return true + } + + return false +} + +// SetEntitiesAllowFreeForm gets a reference to the given NullableBool and assigns it to the EntitiesAllowFreeForm field. +func (o *BankTemplateConfig) SetEntitiesAllowFreeForm(v bool) { + o.EntitiesAllowFreeForm.Set(&v) +} +// SetEntitiesAllowFreeFormNil sets the value for EntitiesAllowFreeForm to be an explicit nil +func (o *BankTemplateConfig) SetEntitiesAllowFreeFormNil() { + o.EntitiesAllowFreeForm.Set(nil) +} + +// UnsetEntitiesAllowFreeForm ensures that no value is present for EntitiesAllowFreeForm, not even an explicit nil +func (o *BankTemplateConfig) UnsetEntitiesAllowFreeForm() { + o.EntitiesAllowFreeForm.Unset() +} + +func (o BankTemplateConfig) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BankTemplateConfig) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if o.ReflectMission.IsSet() { + toSerialize["reflect_mission"] = o.ReflectMission.Get() + } + if o.RetainMission.IsSet() { + toSerialize["retain_mission"] = o.RetainMission.Get() + } + if o.RetainExtractionMode.IsSet() { + toSerialize["retain_extraction_mode"] = o.RetainExtractionMode.Get() + } + if o.RetainCustomInstructions.IsSet() { + toSerialize["retain_custom_instructions"] = o.RetainCustomInstructions.Get() + } + if o.RetainChunkSize.IsSet() { + toSerialize["retain_chunk_size"] = o.RetainChunkSize.Get() + } + if o.EnableObservations.IsSet() { + toSerialize["enable_observations"] = o.EnableObservations.Get() + } + if o.ObservationsMission.IsSet() { + toSerialize["observations_mission"] = o.ObservationsMission.Get() + } + if o.DispositionSkepticism.IsSet() { + toSerialize["disposition_skepticism"] = o.DispositionSkepticism.Get() + } + if o.DispositionLiteralism.IsSet() { + toSerialize["disposition_literalism"] = o.DispositionLiteralism.Get() + } + if o.DispositionEmpathy.IsSet() { + toSerialize["disposition_empathy"] = o.DispositionEmpathy.Get() + } + if o.EntityLabels != nil { + toSerialize["entity_labels"] = o.EntityLabels + } + if o.EntitiesAllowFreeForm.IsSet() { + toSerialize["entities_allow_free_form"] = o.EntitiesAllowFreeForm.Get() + } + return toSerialize, nil +} + +type NullableBankTemplateConfig struct { + value *BankTemplateConfig + isSet bool +} + +func (v NullableBankTemplateConfig) Get() *BankTemplateConfig { + return v.value +} + +func (v *NullableBankTemplateConfig) Set(val *BankTemplateConfig) { + v.value = val + v.isSet = true +} + +func (v NullableBankTemplateConfig) IsSet() bool { + return v.isSet +} + +func (v *NullableBankTemplateConfig) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBankTemplateConfig(val *BankTemplateConfig) *NullableBankTemplateConfig { + return &NullableBankTemplateConfig{value: val, isSet: true} +} + +func (v NullableBankTemplateConfig) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBankTemplateConfig) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/hindsight-clients/go/model_bank_template_directive.go b/hindsight-clients/go/model_bank_template_directive.go new file mode 100644 index 00000000..c0ea18df --- /dev/null +++ b/hindsight-clients/go/model_bank_template_directive.go @@ -0,0 +1,307 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the BankTemplateDirective type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BankTemplateDirective{} + +// BankTemplateDirective A directive definition within a bank template manifest. Directives are matched by name on re-import: existing directives with the same name are updated, new ones are created. +type BankTemplateDirective struct { + // Human-readable name for the directive (used as match key on re-import) + Name string `json:"name"` + // The directive text to inject into prompts + Content string `json:"content"` + // Higher priority directives are injected first + Priority *int32 `json:"priority,omitempty"` + // Whether this directive is active + IsActive *bool `json:"is_active,omitempty"` + // Tags for filtering + Tags []string `json:"tags,omitempty"` +} + +type _BankTemplateDirective BankTemplateDirective + +// NewBankTemplateDirective instantiates a new BankTemplateDirective object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBankTemplateDirective(name string, content string) *BankTemplateDirective { + this := BankTemplateDirective{} + this.Name = name + this.Content = content + var priority int32 = 0 + this.Priority = &priority + var isActive bool = true + this.IsActive = &isActive + return &this +} + +// NewBankTemplateDirectiveWithDefaults instantiates a new BankTemplateDirective object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBankTemplateDirectiveWithDefaults() *BankTemplateDirective { + this := BankTemplateDirective{} + var priority int32 = 0 + this.Priority = &priority + var isActive bool = true + this.IsActive = &isActive + return &this +} + +// GetName returns the Name field value +func (o *BankTemplateDirective) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *BankTemplateDirective) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *BankTemplateDirective) SetName(v string) { + o.Name = v +} + +// GetContent returns the Content field value +func (o *BankTemplateDirective) GetContent() string { + if o == nil { + var ret string + return ret + } + + return o.Content +} + +// GetContentOk returns a tuple with the Content field value +// and a boolean to check if the value has been set. +func (o *BankTemplateDirective) GetContentOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Content, true +} + +// SetContent sets field value +func (o *BankTemplateDirective) SetContent(v string) { + o.Content = v +} + +// GetPriority returns the Priority field value if set, zero value otherwise. +func (o *BankTemplateDirective) GetPriority() int32 { + if o == nil || IsNil(o.Priority) { + var ret int32 + return ret + } + return *o.Priority +} + +// GetPriorityOk returns a tuple with the Priority field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateDirective) GetPriorityOk() (*int32, bool) { + if o == nil || IsNil(o.Priority) { + return nil, false + } + return o.Priority, true +} + +// HasPriority returns a boolean if a field has been set. +func (o *BankTemplateDirective) HasPriority() bool { + if o != nil && !IsNil(o.Priority) { + return true + } + + return false +} + +// SetPriority gets a reference to the given int32 and assigns it to the Priority field. +func (o *BankTemplateDirective) SetPriority(v int32) { + o.Priority = &v +} + +// GetIsActive returns the IsActive field value if set, zero value otherwise. +func (o *BankTemplateDirective) GetIsActive() bool { + if o == nil || IsNil(o.IsActive) { + var ret bool + return ret + } + return *o.IsActive +} + +// GetIsActiveOk returns a tuple with the IsActive field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateDirective) GetIsActiveOk() (*bool, bool) { + if o == nil || IsNil(o.IsActive) { + return nil, false + } + return o.IsActive, true +} + +// HasIsActive returns a boolean if a field has been set. +func (o *BankTemplateDirective) HasIsActive() bool { + if o != nil && !IsNil(o.IsActive) { + return true + } + + return false +} + +// SetIsActive gets a reference to the given bool and assigns it to the IsActive field. +func (o *BankTemplateDirective) SetIsActive(v bool) { + o.IsActive = &v +} + +// GetTags returns the Tags field value if set, zero value otherwise. +func (o *BankTemplateDirective) GetTags() []string { + if o == nil || IsNil(o.Tags) { + var ret []string + return ret + } + return o.Tags +} + +// GetTagsOk returns a tuple with the Tags field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateDirective) GetTagsOk() ([]string, bool) { + if o == nil || IsNil(o.Tags) { + return nil, false + } + return o.Tags, true +} + +// HasTags returns a boolean if a field has been set. +func (o *BankTemplateDirective) HasTags() bool { + if o != nil && !IsNil(o.Tags) { + return true + } + + return false +} + +// SetTags gets a reference to the given []string and assigns it to the Tags field. +func (o *BankTemplateDirective) SetTags(v []string) { + o.Tags = v +} + +func (o BankTemplateDirective) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BankTemplateDirective) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["name"] = o.Name + toSerialize["content"] = o.Content + if !IsNil(o.Priority) { + toSerialize["priority"] = o.Priority + } + if !IsNil(o.IsActive) { + toSerialize["is_active"] = o.IsActive + } + if !IsNil(o.Tags) { + toSerialize["tags"] = o.Tags + } + return toSerialize, nil +} + +func (o *BankTemplateDirective) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "name", + "content", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBankTemplateDirective := _BankTemplateDirective{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varBankTemplateDirective) + + if err != nil { + return err + } + + *o = BankTemplateDirective(varBankTemplateDirective) + + return err +} + +type NullableBankTemplateDirective struct { + value *BankTemplateDirective + isSet bool +} + +func (v NullableBankTemplateDirective) Get() *BankTemplateDirective { + return v.value +} + +func (v *NullableBankTemplateDirective) Set(val *BankTemplateDirective) { + v.value = val + v.isSet = true +} + +func (v NullableBankTemplateDirective) IsSet() bool { + return v.isSet +} + +func (v *NullableBankTemplateDirective) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBankTemplateDirective(val *BankTemplateDirective) *NullableBankTemplateDirective { + return &NullableBankTemplateDirective{value: val, isSet: true} +} + +func (v NullableBankTemplateDirective) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBankTemplateDirective) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/hindsight-clients/go/model_bank_template_import_response.go b/hindsight-clients/go/model_bank_template_import_response.go new file mode 100644 index 00000000..6adbeb2a --- /dev/null +++ b/hindsight-clients/go/model_bank_template_import_response.go @@ -0,0 +1,414 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the BankTemplateImportResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BankTemplateImportResponse{} + +// BankTemplateImportResponse Response model for the bank template import endpoint. +type BankTemplateImportResponse struct { + // Bank that was imported into + BankId string `json:"bank_id"` + // Whether bank config was updated + ConfigApplied bool `json:"config_applied"` + // IDs of newly created mental models + MentalModelsCreated []string `json:"mental_models_created,omitempty"` + // IDs of updated mental models + MentalModelsUpdated []string `json:"mental_models_updated,omitempty"` + // Names of newly created directives + DirectivesCreated []string `json:"directives_created,omitempty"` + // Names of updated directives + DirectivesUpdated []string `json:"directives_updated,omitempty"` + // Operation IDs for mental model content generation (async) + OperationIds []string `json:"operation_ids,omitempty"` + // True if this was a validation-only run + DryRun *bool `json:"dry_run,omitempty"` +} + +type _BankTemplateImportResponse BankTemplateImportResponse + +// NewBankTemplateImportResponse instantiates a new BankTemplateImportResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBankTemplateImportResponse(bankId string, configApplied bool) *BankTemplateImportResponse { + this := BankTemplateImportResponse{} + this.BankId = bankId + this.ConfigApplied = configApplied + var dryRun bool = false + this.DryRun = &dryRun + return &this +} + +// NewBankTemplateImportResponseWithDefaults instantiates a new BankTemplateImportResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBankTemplateImportResponseWithDefaults() *BankTemplateImportResponse { + this := BankTemplateImportResponse{} + var dryRun bool = false + this.DryRun = &dryRun + return &this +} + +// GetBankId returns the BankId field value +func (o *BankTemplateImportResponse) GetBankId() string { + if o == nil { + var ret string + return ret + } + + return o.BankId +} + +// GetBankIdOk returns a tuple with the BankId field value +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetBankIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.BankId, true +} + +// SetBankId sets field value +func (o *BankTemplateImportResponse) SetBankId(v string) { + o.BankId = v +} + +// GetConfigApplied returns the ConfigApplied field value +func (o *BankTemplateImportResponse) GetConfigApplied() bool { + if o == nil { + var ret bool + return ret + } + + return o.ConfigApplied +} + +// GetConfigAppliedOk returns a tuple with the ConfigApplied field value +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetConfigAppliedOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.ConfigApplied, true +} + +// SetConfigApplied sets field value +func (o *BankTemplateImportResponse) SetConfigApplied(v bool) { + o.ConfigApplied = v +} + +// GetMentalModelsCreated returns the MentalModelsCreated field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetMentalModelsCreated() []string { + if o == nil || IsNil(o.MentalModelsCreated) { + var ret []string + return ret + } + return o.MentalModelsCreated +} + +// GetMentalModelsCreatedOk returns a tuple with the MentalModelsCreated field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetMentalModelsCreatedOk() ([]string, bool) { + if o == nil || IsNil(o.MentalModelsCreated) { + return nil, false + } + return o.MentalModelsCreated, true +} + +// HasMentalModelsCreated returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasMentalModelsCreated() bool { + if o != nil && !IsNil(o.MentalModelsCreated) { + return true + } + + return false +} + +// SetMentalModelsCreated gets a reference to the given []string and assigns it to the MentalModelsCreated field. +func (o *BankTemplateImportResponse) SetMentalModelsCreated(v []string) { + o.MentalModelsCreated = v +} + +// GetMentalModelsUpdated returns the MentalModelsUpdated field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetMentalModelsUpdated() []string { + if o == nil || IsNil(o.MentalModelsUpdated) { + var ret []string + return ret + } + return o.MentalModelsUpdated +} + +// GetMentalModelsUpdatedOk returns a tuple with the MentalModelsUpdated field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetMentalModelsUpdatedOk() ([]string, bool) { + if o == nil || IsNil(o.MentalModelsUpdated) { + return nil, false + } + return o.MentalModelsUpdated, true +} + +// HasMentalModelsUpdated returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasMentalModelsUpdated() bool { + if o != nil && !IsNil(o.MentalModelsUpdated) { + return true + } + + return false +} + +// SetMentalModelsUpdated gets a reference to the given []string and assigns it to the MentalModelsUpdated field. +func (o *BankTemplateImportResponse) SetMentalModelsUpdated(v []string) { + o.MentalModelsUpdated = v +} + +// GetDirectivesCreated returns the DirectivesCreated field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetDirectivesCreated() []string { + if o == nil || IsNil(o.DirectivesCreated) { + var ret []string + return ret + } + return o.DirectivesCreated +} + +// GetDirectivesCreatedOk returns a tuple with the DirectivesCreated field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetDirectivesCreatedOk() ([]string, bool) { + if o == nil || IsNil(o.DirectivesCreated) { + return nil, false + } + return o.DirectivesCreated, true +} + +// HasDirectivesCreated returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasDirectivesCreated() bool { + if o != nil && !IsNil(o.DirectivesCreated) { + return true + } + + return false +} + +// SetDirectivesCreated gets a reference to the given []string and assigns it to the DirectivesCreated field. +func (o *BankTemplateImportResponse) SetDirectivesCreated(v []string) { + o.DirectivesCreated = v +} + +// GetDirectivesUpdated returns the DirectivesUpdated field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetDirectivesUpdated() []string { + if o == nil || IsNil(o.DirectivesUpdated) { + var ret []string + return ret + } + return o.DirectivesUpdated +} + +// GetDirectivesUpdatedOk returns a tuple with the DirectivesUpdated field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetDirectivesUpdatedOk() ([]string, bool) { + if o == nil || IsNil(o.DirectivesUpdated) { + return nil, false + } + return o.DirectivesUpdated, true +} + +// HasDirectivesUpdated returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasDirectivesUpdated() bool { + if o != nil && !IsNil(o.DirectivesUpdated) { + return true + } + + return false +} + +// SetDirectivesUpdated gets a reference to the given []string and assigns it to the DirectivesUpdated field. +func (o *BankTemplateImportResponse) SetDirectivesUpdated(v []string) { + o.DirectivesUpdated = v +} + +// GetOperationIds returns the OperationIds field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetOperationIds() []string { + if o == nil || IsNil(o.OperationIds) { + var ret []string + return ret + } + return o.OperationIds +} + +// GetOperationIdsOk returns a tuple with the OperationIds field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetOperationIdsOk() ([]string, bool) { + if o == nil || IsNil(o.OperationIds) { + return nil, false + } + return o.OperationIds, true +} + +// HasOperationIds returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasOperationIds() bool { + if o != nil && !IsNil(o.OperationIds) { + return true + } + + return false +} + +// SetOperationIds gets a reference to the given []string and assigns it to the OperationIds field. +func (o *BankTemplateImportResponse) SetOperationIds(v []string) { + o.OperationIds = v +} + +// GetDryRun returns the DryRun field value if set, zero value otherwise. +func (o *BankTemplateImportResponse) GetDryRun() bool { + if o == nil || IsNil(o.DryRun) { + var ret bool + return ret + } + return *o.DryRun +} + +// GetDryRunOk returns a tuple with the DryRun field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateImportResponse) GetDryRunOk() (*bool, bool) { + if o == nil || IsNil(o.DryRun) { + return nil, false + } + return o.DryRun, true +} + +// HasDryRun returns a boolean if a field has been set. +func (o *BankTemplateImportResponse) HasDryRun() bool { + if o != nil && !IsNil(o.DryRun) { + return true + } + + return false +} + +// SetDryRun gets a reference to the given bool and assigns it to the DryRun field. +func (o *BankTemplateImportResponse) SetDryRun(v bool) { + o.DryRun = &v +} + +func (o BankTemplateImportResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BankTemplateImportResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["bank_id"] = o.BankId + toSerialize["config_applied"] = o.ConfigApplied + if !IsNil(o.MentalModelsCreated) { + toSerialize["mental_models_created"] = o.MentalModelsCreated + } + if !IsNil(o.MentalModelsUpdated) { + toSerialize["mental_models_updated"] = o.MentalModelsUpdated + } + if !IsNil(o.DirectivesCreated) { + toSerialize["directives_created"] = o.DirectivesCreated + } + if !IsNil(o.DirectivesUpdated) { + toSerialize["directives_updated"] = o.DirectivesUpdated + } + if !IsNil(o.OperationIds) { + toSerialize["operation_ids"] = o.OperationIds + } + if !IsNil(o.DryRun) { + toSerialize["dry_run"] = o.DryRun + } + return toSerialize, nil +} + +func (o *BankTemplateImportResponse) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "bank_id", + "config_applied", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBankTemplateImportResponse := _BankTemplateImportResponse{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varBankTemplateImportResponse) + + if err != nil { + return err + } + + *o = BankTemplateImportResponse(varBankTemplateImportResponse) + + return err +} + +type NullableBankTemplateImportResponse struct { + value *BankTemplateImportResponse + isSet bool +} + +func (v NullableBankTemplateImportResponse) Get() *BankTemplateImportResponse { + return v.value +} + +func (v *NullableBankTemplateImportResponse) Set(val *BankTemplateImportResponse) { + v.value = val + v.isSet = true +} + +func (v NullableBankTemplateImportResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableBankTemplateImportResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBankTemplateImportResponse(val *BankTemplateImportResponse) *NullableBankTemplateImportResponse { + return &NullableBankTemplateImportResponse{value: val, isSet: true} +} + +func (v NullableBankTemplateImportResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBankTemplateImportResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/hindsight-clients/go/model_bank_template_manifest.go b/hindsight-clients/go/model_bank_template_manifest.go new file mode 100644 index 00000000..6983f9e8 --- /dev/null +++ b/hindsight-clients/go/model_bank_template_manifest.go @@ -0,0 +1,279 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the BankTemplateManifest type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BankTemplateManifest{} + +// BankTemplateManifest A bank template manifest for import/export. Version field enables forward-compatible schema evolution: the API auto-upgrades older manifest versions to the current schema on import. +type BankTemplateManifest struct { + // Manifest schema version (currently '1') + Version string `json:"version"` + Bank NullableBankTemplateConfig `json:"bank,omitempty"` + MentalModels []BankTemplateMentalModel `json:"mental_models,omitempty"` + Directives []BankTemplateDirective `json:"directives,omitempty"` +} + +type _BankTemplateManifest BankTemplateManifest + +// NewBankTemplateManifest instantiates a new BankTemplateManifest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBankTemplateManifest(version string) *BankTemplateManifest { + this := BankTemplateManifest{} + this.Version = version + return &this +} + +// NewBankTemplateManifestWithDefaults instantiates a new BankTemplateManifest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBankTemplateManifestWithDefaults() *BankTemplateManifest { + this := BankTemplateManifest{} + return &this +} + +// GetVersion returns the Version field value +func (o *BankTemplateManifest) GetVersion() string { + if o == nil { + var ret string + return ret + } + + return o.Version +} + +// GetVersionOk returns a tuple with the Version field value +// and a boolean to check if the value has been set. +func (o *BankTemplateManifest) GetVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Version, true +} + +// SetVersion sets field value +func (o *BankTemplateManifest) SetVersion(v string) { + o.Version = v +} + +// GetBank returns the Bank field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateManifest) GetBank() BankTemplateConfig { + if o == nil || IsNil(o.Bank.Get()) { + var ret BankTemplateConfig + return ret + } + return *o.Bank.Get() +} + +// GetBankOk returns a tuple with the Bank field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateManifest) GetBankOk() (*BankTemplateConfig, bool) { + if o == nil { + return nil, false + } + return o.Bank.Get(), o.Bank.IsSet() +} + +// HasBank returns a boolean if a field has been set. +func (o *BankTemplateManifest) HasBank() bool { + if o != nil && o.Bank.IsSet() { + return true + } + + return false +} + +// SetBank gets a reference to the given NullableBankTemplateConfig and assigns it to the Bank field. +func (o *BankTemplateManifest) SetBank(v BankTemplateConfig) { + o.Bank.Set(&v) +} +// SetBankNil sets the value for Bank to be an explicit nil +func (o *BankTemplateManifest) SetBankNil() { + o.Bank.Set(nil) +} + +// UnsetBank ensures that no value is present for Bank, not even an explicit nil +func (o *BankTemplateManifest) UnsetBank() { + o.Bank.Unset() +} + +// GetMentalModels returns the MentalModels field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateManifest) GetMentalModels() []BankTemplateMentalModel { + if o == nil { + var ret []BankTemplateMentalModel + return ret + } + return o.MentalModels +} + +// GetMentalModelsOk returns a tuple with the MentalModels field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateManifest) GetMentalModelsOk() ([]BankTemplateMentalModel, bool) { + if o == nil || IsNil(o.MentalModels) { + return nil, false + } + return o.MentalModels, true +} + +// HasMentalModels returns a boolean if a field has been set. +func (o *BankTemplateManifest) HasMentalModels() bool { + if o != nil && !IsNil(o.MentalModels) { + return true + } + + return false +} + +// SetMentalModels gets a reference to the given []BankTemplateMentalModel and assigns it to the MentalModels field. +func (o *BankTemplateManifest) SetMentalModels(v []BankTemplateMentalModel) { + o.MentalModels = v +} + +// GetDirectives returns the Directives field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *BankTemplateManifest) GetDirectives() []BankTemplateDirective { + if o == nil { + var ret []BankTemplateDirective + return ret + } + return o.Directives +} + +// GetDirectivesOk returns a tuple with the Directives field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *BankTemplateManifest) GetDirectivesOk() ([]BankTemplateDirective, bool) { + if o == nil || IsNil(o.Directives) { + return nil, false + } + return o.Directives, true +} + +// HasDirectives returns a boolean if a field has been set. +func (o *BankTemplateManifest) HasDirectives() bool { + if o != nil && !IsNil(o.Directives) { + return true + } + + return false +} + +// SetDirectives gets a reference to the given []BankTemplateDirective and assigns it to the Directives field. +func (o *BankTemplateManifest) SetDirectives(v []BankTemplateDirective) { + o.Directives = v +} + +func (o BankTemplateManifest) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BankTemplateManifest) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["version"] = o.Version + if o.Bank.IsSet() { + toSerialize["bank"] = o.Bank.Get() + } + if o.MentalModels != nil { + toSerialize["mental_models"] = o.MentalModels + } + if o.Directives != nil { + toSerialize["directives"] = o.Directives + } + return toSerialize, nil +} + +func (o *BankTemplateManifest) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "version", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBankTemplateManifest := _BankTemplateManifest{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varBankTemplateManifest) + + if err != nil { + return err + } + + *o = BankTemplateManifest(varBankTemplateManifest) + + return err +} + +type NullableBankTemplateManifest struct { + value *BankTemplateManifest + isSet bool +} + +func (v NullableBankTemplateManifest) Get() *BankTemplateManifest { + return v.value +} + +func (v *NullableBankTemplateManifest) Set(val *BankTemplateManifest) { + v.value = val + v.isSet = true +} + +func (v NullableBankTemplateManifest) IsSet() bool { + return v.isSet +} + +func (v *NullableBankTemplateManifest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBankTemplateManifest(val *BankTemplateManifest) *NullableBankTemplateManifest { + return &NullableBankTemplateManifest{value: val, isSet: true} +} + +func (v NullableBankTemplateManifest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBankTemplateManifest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/hindsight-clients/go/model_bank_template_mental_model.go b/hindsight-clients/go/model_bank_template_mental_model.go new file mode 100644 index 00000000..8c822629 --- /dev/null +++ b/hindsight-clients/go/model_bank_template_mental_model.go @@ -0,0 +1,332 @@ +/* +Hindsight HTTP API + +HTTP API for Hindsight + +API version: 0.4.22 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package hindsight + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the BankTemplateMentalModel type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BankTemplateMentalModel{} + +// BankTemplateMentalModel A mental model definition within a bank template manifest. +type BankTemplateMentalModel struct { + // Unique ID for the mental model (alphanumeric lowercase with hyphens) + Id string `json:"id"` + // Human-readable name for the mental model + Name string `json:"name"` + // The query to run to generate content + SourceQuery string `json:"source_query"` + // Tags for scoped visibility + Tags []string `json:"tags,omitempty"` + // Maximum tokens for generated content + MaxTokens *int32 `json:"max_tokens,omitempty"` + // Trigger settings + Trigger *MentalModelTriggerOutput `json:"trigger,omitempty"` +} + +type _BankTemplateMentalModel BankTemplateMentalModel + +// NewBankTemplateMentalModel instantiates a new BankTemplateMentalModel object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBankTemplateMentalModel(id string, name string, sourceQuery string) *BankTemplateMentalModel { + this := BankTemplateMentalModel{} + this.Id = id + this.Name = name + this.SourceQuery = sourceQuery + var maxTokens int32 = 2048 + this.MaxTokens = &maxTokens + return &this +} + +// NewBankTemplateMentalModelWithDefaults instantiates a new BankTemplateMentalModel object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBankTemplateMentalModelWithDefaults() *BankTemplateMentalModel { + this := BankTemplateMentalModel{} + var maxTokens int32 = 2048 + this.MaxTokens = &maxTokens + return &this +} + +// GetId returns the Id field value +func (o *BankTemplateMentalModel) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *BankTemplateMentalModel) SetId(v string) { + o.Id = v +} + +// GetName returns the Name field value +func (o *BankTemplateMentalModel) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *BankTemplateMentalModel) SetName(v string) { + o.Name = v +} + +// GetSourceQuery returns the SourceQuery field value +func (o *BankTemplateMentalModel) GetSourceQuery() string { + if o == nil { + var ret string + return ret + } + + return o.SourceQuery +} + +// GetSourceQueryOk returns a tuple with the SourceQuery field value +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetSourceQueryOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.SourceQuery, true +} + +// SetSourceQuery sets field value +func (o *BankTemplateMentalModel) SetSourceQuery(v string) { + o.SourceQuery = v +} + +// GetTags returns the Tags field value if set, zero value otherwise. +func (o *BankTemplateMentalModel) GetTags() []string { + if o == nil || IsNil(o.Tags) { + var ret []string + return ret + } + return o.Tags +} + +// GetTagsOk returns a tuple with the Tags field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetTagsOk() ([]string, bool) { + if o == nil || IsNil(o.Tags) { + return nil, false + } + return o.Tags, true +} + +// HasTags returns a boolean if a field has been set. +func (o *BankTemplateMentalModel) HasTags() bool { + if o != nil && !IsNil(o.Tags) { + return true + } + + return false +} + +// SetTags gets a reference to the given []string and assigns it to the Tags field. +func (o *BankTemplateMentalModel) SetTags(v []string) { + o.Tags = v +} + +// GetMaxTokens returns the MaxTokens field value if set, zero value otherwise. +func (o *BankTemplateMentalModel) GetMaxTokens() int32 { + if o == nil || IsNil(o.MaxTokens) { + var ret int32 + return ret + } + return *o.MaxTokens +} + +// GetMaxTokensOk returns a tuple with the MaxTokens field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetMaxTokensOk() (*int32, bool) { + if o == nil || IsNil(o.MaxTokens) { + return nil, false + } + return o.MaxTokens, true +} + +// HasMaxTokens returns a boolean if a field has been set. +func (o *BankTemplateMentalModel) HasMaxTokens() bool { + if o != nil && !IsNil(o.MaxTokens) { + return true + } + + return false +} + +// SetMaxTokens gets a reference to the given int32 and assigns it to the MaxTokens field. +func (o *BankTemplateMentalModel) SetMaxTokens(v int32) { + o.MaxTokens = &v +} + +// GetTrigger returns the Trigger field value if set, zero value otherwise. +func (o *BankTemplateMentalModel) GetTrigger() MentalModelTriggerOutput { + if o == nil || IsNil(o.Trigger) { + var ret MentalModelTriggerOutput + return ret + } + return *o.Trigger +} + +// GetTriggerOk returns a tuple with the Trigger field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BankTemplateMentalModel) GetTriggerOk() (*MentalModelTriggerOutput, bool) { + if o == nil || IsNil(o.Trigger) { + return nil, false + } + return o.Trigger, true +} + +// HasTrigger returns a boolean if a field has been set. +func (o *BankTemplateMentalModel) HasTrigger() bool { + if o != nil && !IsNil(o.Trigger) { + return true + } + + return false +} + +// SetTrigger gets a reference to the given MentalModelTriggerOutput and assigns it to the Trigger field. +func (o *BankTemplateMentalModel) SetTrigger(v MentalModelTriggerOutput) { + o.Trigger = &v +} + +func (o BankTemplateMentalModel) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BankTemplateMentalModel) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["name"] = o.Name + toSerialize["source_query"] = o.SourceQuery + if !IsNil(o.Tags) { + toSerialize["tags"] = o.Tags + } + if !IsNil(o.MaxTokens) { + toSerialize["max_tokens"] = o.MaxTokens + } + if !IsNil(o.Trigger) { + toSerialize["trigger"] = o.Trigger + } + return toSerialize, nil +} + +func (o *BankTemplateMentalModel) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "name", + "source_query", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBankTemplateMentalModel := _BankTemplateMentalModel{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varBankTemplateMentalModel) + + if err != nil { + return err + } + + *o = BankTemplateMentalModel(varBankTemplateMentalModel) + + return err +} + +type NullableBankTemplateMentalModel struct { + value *BankTemplateMentalModel + isSet bool +} + +func (v NullableBankTemplateMentalModel) Get() *BankTemplateMentalModel { + return v.value +} + +func (v *NullableBankTemplateMentalModel) Set(val *BankTemplateMentalModel) { + v.value = val + v.isSet = true +} + +func (v NullableBankTemplateMentalModel) IsSet() bool { + return v.isSet +} + +func (v *NullableBankTemplateMentalModel) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBankTemplateMentalModel(val *BankTemplateMentalModel) *NullableBankTemplateMentalModel { + return &NullableBankTemplateMentalModel{value: val, isSet: true} +} + +func (v NullableBankTemplateMentalModel) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBankTemplateMentalModel) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/hindsight-clients/python/.openapi-generator/FILES b/hindsight-clients/python/.openapi-generator/FILES index 4613671a..5dfb7927 100644 --- a/hindsight-clients/python/.openapi-generator/FILES +++ b/hindsight-clients/python/.openapi-generator/FILES @@ -1,6 +1,7 @@ hindsight_client_api/__init__.py hindsight_client_api/api/__init__.py hindsight_client_api/api/audit_api.py +hindsight_client_api/api/bank_templates_api.py hindsight_client_api/api/banks_api.py hindsight_client_api/api/directives_api.py hindsight_client_api/api/documents_api.py @@ -29,6 +30,11 @@ hindsight_client_api/models/bank_list_item.py hindsight_client_api/models/bank_list_response.py hindsight_client_api/models/bank_profile_response.py hindsight_client_api/models/bank_stats_response.py +hindsight_client_api/models/bank_template_config.py +hindsight_client_api/models/bank_template_directive.py +hindsight_client_api/models/bank_template_import_response.py +hindsight_client_api/models/bank_template_manifest.py +hindsight_client_api/models/bank_template_mental_model.py hindsight_client_api/models/budget.py hindsight_client_api/models/cancel_operation_response.py hindsight_client_api/models/child_operation_status.py diff --git a/hindsight-clients/python/hindsight_client_api/__init__.py b/hindsight-clients/python/hindsight_client_api/__init__.py index db0e2221..a90f3d75 100644 --- a/hindsight-clients/python/hindsight_client_api/__init__.py +++ b/hindsight-clients/python/hindsight_client_api/__init__.py @@ -18,6 +18,7 @@ __version__ = "0.0.7" # import apis into sdk package from hindsight_client_api.api.audit_api import AuditApi +from hindsight_client_api.api.bank_templates_api import BankTemplatesApi from hindsight_client_api.api.banks_api import BanksApi from hindsight_client_api.api.directives_api import DirectivesApi from hindsight_client_api.api.documents_api import DocumentsApi @@ -54,6 +55,11 @@ from hindsight_client_api.models.bank_list_item import BankListItem from hindsight_client_api.models.bank_list_response import BankListResponse from hindsight_client_api.models.bank_profile_response import BankProfileResponse from hindsight_client_api.models.bank_stats_response import BankStatsResponse +from hindsight_client_api.models.bank_template_config import BankTemplateConfig +from hindsight_client_api.models.bank_template_directive import BankTemplateDirective +from hindsight_client_api.models.bank_template_import_response import BankTemplateImportResponse +from hindsight_client_api.models.bank_template_manifest import BankTemplateManifest +from hindsight_client_api.models.bank_template_mental_model import BankTemplateMentalModel from hindsight_client_api.models.budget import Budget from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse from hindsight_client_api.models.child_operation_status import ChildOperationStatus diff --git a/hindsight-clients/python/hindsight_client_api/api/__init__.py b/hindsight-clients/python/hindsight_client_api/api/__init__.py index 7573e5e7..fc09c82a 100644 --- a/hindsight-clients/python/hindsight_client_api/api/__init__.py +++ b/hindsight-clients/python/hindsight_client_api/api/__init__.py @@ -2,6 +2,7 @@ # import apis into api package from hindsight_client_api.api.audit_api import AuditApi +from hindsight_client_api.api.bank_templates_api import BankTemplatesApi from hindsight_client_api.api.banks_api import BanksApi from hindsight_client_api.api.directives_api import DirectivesApi from hindsight_client_api.api.documents_api import DocumentsApi diff --git a/hindsight-clients/python/hindsight_client_api/api/bank_templates_api.py b/hindsight-clients/python/hindsight_client_api/api/bank_templates_api.py new file mode 100644 index 00000000..ed308431 --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/api/bank_templates_api.py @@ -0,0 +1,858 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + +import warnings +from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt +from typing import Any, Dict, List, Optional, Tuple, Union +from typing_extensions import Annotated + +from pydantic import Field, StrictBool, StrictStr +from typing import Any, Optional +from typing_extensions import Annotated +from hindsight_client_api.models.bank_template_import_response import BankTemplateImportResponse +from hindsight_client_api.models.bank_template_manifest import BankTemplateManifest + +from hindsight_client_api.api_client import ApiClient, RequestSerialized +from hindsight_client_api.api_response import ApiResponse +from hindsight_client_api.rest import RESTResponseType + + +class BankTemplatesApi: + """NOTE: This class is auto generated by OpenAPI Generator + Ref: https://openapi-generator.tech + + Do not edit the class manually. + """ + + def __init__(self, api_client=None) -> None: + if api_client is None: + api_client = ApiClient.get_default() + self.api_client = api_client + + + @validate_call + async def export_bank_template( + self, + bank_id: StrictStr, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> BankTemplateManifest: + """Export bank template + + Export a bank's current configuration, mental models, and directives as a template manifest. The exported manifest can be imported into another bank to replicate the setup. + + :param bank_id: (required) + :type bank_id: str + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._export_bank_template_serialize( + bank_id=bank_id, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateManifest", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def export_bank_template_with_http_info( + self, + bank_id: StrictStr, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[BankTemplateManifest]: + """Export bank template + + Export a bank's current configuration, mental models, and directives as a template manifest. The exported manifest can be imported into another bank to replicate the setup. + + :param bank_id: (required) + :type bank_id: str + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._export_bank_template_serialize( + bank_id=bank_id, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateManifest", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def export_bank_template_without_preload_content( + self, + bank_id: StrictStr, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Export bank template + + Export a bank's current configuration, mental models, and directives as a template manifest. The exported manifest can be imported into another bank to replicate the setup. + + :param bank_id: (required) + :type bank_id: str + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._export_bank_template_serialize( + bank_id=bank_id, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateManifest", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _export_bank_template_serialize( + self, + bank_id, + authorization, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + if bank_id is not None: + _path_params['bank_id'] = bank_id + # process the query parameters + # process the header parameters + if authorization is not None: + _header_params['authorization'] = authorization + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/v1/default/banks/{bank_id}/export', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + async def get_bank_template_schema( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> object: + """Get bank template JSON Schema + + Returns the JSON Schema for the bank template manifest format. Use this to validate template manifests before importing. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._get_bank_template_schema_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def get_bank_template_schema_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[object]: + """Get bank template JSON Schema + + Returns the JSON Schema for the bank template manifest format. Use this to validate template manifests before importing. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._get_bank_template_schema_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def get_bank_template_schema_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Get bank template JSON Schema + + Returns the JSON Schema for the bank template manifest format. Use this to validate template manifests before importing. + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._get_bank_template_schema_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _get_bank_template_schema_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/v1/bank-template-schema', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + async def import_bank_template( + self, + bank_id: StrictStr, + dry_run: Annotated[Optional[StrictBool], Field(description="Validate only, do not apply changes")] = None, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> BankTemplateImportResponse: + """Import bank template + + Import a bank template manifest to create or update a bank's configuration, mental models, and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. Mental models are matched by id, directives by name — existing ones are updated, new ones are created. Use dry_run=true to validate the manifest without applying changes. + + :param bank_id: (required) + :type bank_id: str + :param dry_run: Validate only, do not apply changes + :type dry_run: bool + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._import_bank_template_serialize( + bank_id=bank_id, + dry_run=dry_run, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateImportResponse", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + async def import_bank_template_with_http_info( + self, + bank_id: StrictStr, + dry_run: Annotated[Optional[StrictBool], Field(description="Validate only, do not apply changes")] = None, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[BankTemplateImportResponse]: + """Import bank template + + Import a bank template manifest to create or update a bank's configuration, mental models, and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. Mental models are matched by id, directives by name — existing ones are updated, new ones are created. Use dry_run=true to validate the manifest without applying changes. + + :param bank_id: (required) + :type bank_id: str + :param dry_run: Validate only, do not apply changes + :type dry_run: bool + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._import_bank_template_serialize( + bank_id=bank_id, + dry_run=dry_run, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateImportResponse", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + await response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + async def import_bank_template_without_preload_content( + self, + bank_id: StrictStr, + dry_run: Annotated[Optional[StrictBool], Field(description="Validate only, do not apply changes")] = None, + authorization: Optional[StrictStr] = None, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Import bank template + + Import a bank template manifest to create or update a bank's configuration, mental models, and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. Mental models are matched by id, directives by name — existing ones are updated, new ones are created. Use dry_run=true to validate the manifest without applying changes. + + :param bank_id: (required) + :type bank_id: str + :param dry_run: Validate only, do not apply changes + :type dry_run: bool + :param authorization: + :type authorization: str + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._import_bank_template_serialize( + bank_id=bank_id, + dry_run=dry_run, + authorization=authorization, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "BankTemplateImportResponse", + '422': "HTTPValidationError", + } + response_data = await self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _import_bank_template_serialize( + self, + bank_id, + dry_run, + authorization, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + if bank_id is not None: + _path_params['bank_id'] = bank_id + # process the query parameters + if dry_run is not None: + + _query_params.append(('dry_run', dry_run)) + + # process the header parameters + if authorization is not None: + _header_params['authorization'] = authorization + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/v1/default/banks/{bank_id}/import', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + diff --git a/hindsight-clients/python/hindsight_client_api/models/__init__.py b/hindsight-clients/python/hindsight_client_api/models/__init__.py index 27c0947b..968af4d2 100644 --- a/hindsight-clients/python/hindsight_client_api/models/__init__.py +++ b/hindsight-clients/python/hindsight_client_api/models/__init__.py @@ -27,6 +27,11 @@ from hindsight_client_api.models.bank_list_item import BankListItem from hindsight_client_api.models.bank_list_response import BankListResponse from hindsight_client_api.models.bank_profile_response import BankProfileResponse from hindsight_client_api.models.bank_stats_response import BankStatsResponse +from hindsight_client_api.models.bank_template_config import BankTemplateConfig +from hindsight_client_api.models.bank_template_directive import BankTemplateDirective +from hindsight_client_api.models.bank_template_import_response import BankTemplateImportResponse +from hindsight_client_api.models.bank_template_manifest import BankTemplateManifest +from hindsight_client_api.models.bank_template_mental_model import BankTemplateMentalModel from hindsight_client_api.models.budget import Budget from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse from hindsight_client_api.models.child_operation_status import ChildOperationStatus diff --git a/hindsight-clients/python/hindsight_client_api/models/bank_template_config.py b/hindsight-clients/python/hindsight_client_api/models/bank_template_config.py new file mode 100644 index 00000000..c5ebfd9e --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/models/bank_template_config.py @@ -0,0 +1,170 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from typing_extensions import Annotated +from typing import Optional, Set +from typing_extensions import Self + +class BankTemplateConfig(BaseModel): + """ + Bank configuration fields within a template manifest. Only includes configurable (per-bank) fields. Credential fields (API keys, base URLs) are intentionally excluded for security. + """ # noqa: E501 + reflect_mission: Optional[StrictStr] = None + retain_mission: Optional[StrictStr] = None + retain_extraction_mode: Optional[StrictStr] = None + retain_custom_instructions: Optional[StrictStr] = None + retain_chunk_size: Optional[StrictInt] = None + enable_observations: Optional[StrictBool] = None + observations_mission: Optional[StrictStr] = None + disposition_skepticism: Optional[Annotated[int, Field(le=5, strict=True, ge=1)]] = None + disposition_literalism: Optional[Annotated[int, Field(le=5, strict=True, ge=1)]] = None + disposition_empathy: Optional[Annotated[int, Field(le=5, strict=True, ge=1)]] = None + entity_labels: Optional[List[StrictStr]] = None + entities_allow_free_form: Optional[StrictBool] = None + __properties: ClassVar[List[str]] = ["reflect_mission", "retain_mission", "retain_extraction_mode", "retain_custom_instructions", "retain_chunk_size", "enable_observations", "observations_mission", "disposition_skepticism", "disposition_literalism", "disposition_empathy", "entity_labels", "entities_allow_free_form"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BankTemplateConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if reflect_mission (nullable) is None + # and model_fields_set contains the field + if self.reflect_mission is None and "reflect_mission" in self.model_fields_set: + _dict['reflect_mission'] = None + + # set to None if retain_mission (nullable) is None + # and model_fields_set contains the field + if self.retain_mission is None and "retain_mission" in self.model_fields_set: + _dict['retain_mission'] = None + + # set to None if retain_extraction_mode (nullable) is None + # and model_fields_set contains the field + if self.retain_extraction_mode is None and "retain_extraction_mode" in self.model_fields_set: + _dict['retain_extraction_mode'] = None + + # set to None if retain_custom_instructions (nullable) is None + # and model_fields_set contains the field + if self.retain_custom_instructions is None and "retain_custom_instructions" in self.model_fields_set: + _dict['retain_custom_instructions'] = None + + # set to None if retain_chunk_size (nullable) is None + # and model_fields_set contains the field + if self.retain_chunk_size is None and "retain_chunk_size" in self.model_fields_set: + _dict['retain_chunk_size'] = None + + # set to None if enable_observations (nullable) is None + # and model_fields_set contains the field + if self.enable_observations is None and "enable_observations" in self.model_fields_set: + _dict['enable_observations'] = None + + # set to None if observations_mission (nullable) is None + # and model_fields_set contains the field + if self.observations_mission is None and "observations_mission" in self.model_fields_set: + _dict['observations_mission'] = None + + # set to None if disposition_skepticism (nullable) is None + # and model_fields_set contains the field + if self.disposition_skepticism is None and "disposition_skepticism" in self.model_fields_set: + _dict['disposition_skepticism'] = None + + # set to None if disposition_literalism (nullable) is None + # and model_fields_set contains the field + if self.disposition_literalism is None and "disposition_literalism" in self.model_fields_set: + _dict['disposition_literalism'] = None + + # set to None if disposition_empathy (nullable) is None + # and model_fields_set contains the field + if self.disposition_empathy is None and "disposition_empathy" in self.model_fields_set: + _dict['disposition_empathy'] = None + + # set to None if entity_labels (nullable) is None + # and model_fields_set contains the field + if self.entity_labels is None and "entity_labels" in self.model_fields_set: + _dict['entity_labels'] = None + + # set to None if entities_allow_free_form (nullable) is None + # and model_fields_set contains the field + if self.entities_allow_free_form is None and "entities_allow_free_form" in self.model_fields_set: + _dict['entities_allow_free_form'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BankTemplateConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "reflect_mission": obj.get("reflect_mission"), + "retain_mission": obj.get("retain_mission"), + "retain_extraction_mode": obj.get("retain_extraction_mode"), + "retain_custom_instructions": obj.get("retain_custom_instructions"), + "retain_chunk_size": obj.get("retain_chunk_size"), + "enable_observations": obj.get("enable_observations"), + "observations_mission": obj.get("observations_mission"), + "disposition_skepticism": obj.get("disposition_skepticism"), + "disposition_literalism": obj.get("disposition_literalism"), + "disposition_empathy": obj.get("disposition_empathy"), + "entity_labels": obj.get("entity_labels"), + "entities_allow_free_form": obj.get("entities_allow_free_form") + }) + return _obj + + diff --git a/hindsight-clients/python/hindsight_client_api/models/bank_template_directive.py b/hindsight-clients/python/hindsight_client_api/models/bank_template_directive.py new file mode 100644 index 00000000..4e197286 --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/models/bank_template_directive.py @@ -0,0 +1,95 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from typing import Optional, Set +from typing_extensions import Self + +class BankTemplateDirective(BaseModel): + """ + A directive definition within a bank template manifest. Directives are matched by name on re-import: existing directives with the same name are updated, new ones are created. + """ # noqa: E501 + name: StrictStr = Field(description="Human-readable name for the directive (used as match key on re-import)") + content: StrictStr = Field(description="The directive text to inject into prompts") + priority: Optional[StrictInt] = Field(default=0, description="Higher priority directives are injected first") + is_active: Optional[StrictBool] = Field(default=True, description="Whether this directive is active") + tags: Optional[List[StrictStr]] = Field(default=None, description="Tags for filtering") + __properties: ClassVar[List[str]] = ["name", "content", "priority", "is_active", "tags"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BankTemplateDirective from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BankTemplateDirective from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "name": obj.get("name"), + "content": obj.get("content"), + "priority": obj.get("priority") if obj.get("priority") is not None else 0, + "is_active": obj.get("is_active") if obj.get("is_active") is not None else True, + "tags": obj.get("tags") + }) + return _obj + + diff --git a/hindsight-clients/python/hindsight_client_api/models/bank_template_import_response.py b/hindsight-clients/python/hindsight_client_api/models/bank_template_import_response.py new file mode 100644 index 00000000..e31acd7c --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/models/bank_template_import_response.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from typing import Optional, Set +from typing_extensions import Self + +class BankTemplateImportResponse(BaseModel): + """ + Response model for the bank template import endpoint. + """ # noqa: E501 + bank_id: StrictStr = Field(description="Bank that was imported into") + config_applied: StrictBool = Field(description="Whether bank config was updated") + mental_models_created: Optional[List[StrictStr]] = Field(default=None, description="IDs of newly created mental models") + mental_models_updated: Optional[List[StrictStr]] = Field(default=None, description="IDs of updated mental models") + directives_created: Optional[List[StrictStr]] = Field(default=None, description="Names of newly created directives") + directives_updated: Optional[List[StrictStr]] = Field(default=None, description="Names of updated directives") + operation_ids: Optional[List[StrictStr]] = Field(default=None, description="Operation IDs for mental model content generation (async)") + dry_run: Optional[StrictBool] = Field(default=False, description="True if this was a validation-only run") + __properties: ClassVar[List[str]] = ["bank_id", "config_applied", "mental_models_created", "mental_models_updated", "directives_created", "directives_updated", "operation_ids", "dry_run"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BankTemplateImportResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BankTemplateImportResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "bank_id": obj.get("bank_id"), + "config_applied": obj.get("config_applied"), + "mental_models_created": obj.get("mental_models_created"), + "mental_models_updated": obj.get("mental_models_updated"), + "directives_created": obj.get("directives_created"), + "directives_updated": obj.get("directives_updated"), + "operation_ids": obj.get("operation_ids"), + "dry_run": obj.get("dry_run") if obj.get("dry_run") is not None else False + }) + return _obj + + diff --git a/hindsight-clients/python/hindsight_client_api/models/bank_template_manifest.py b/hindsight-clients/python/hindsight_client_api/models/bank_template_manifest.py new file mode 100644 index 00000000..20c7221d --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/models/bank_template_manifest.py @@ -0,0 +1,128 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from hindsight_client_api.models.bank_template_config import BankTemplateConfig +from hindsight_client_api.models.bank_template_directive import BankTemplateDirective +from hindsight_client_api.models.bank_template_mental_model import BankTemplateMentalModel +from typing import Optional, Set +from typing_extensions import Self + +class BankTemplateManifest(BaseModel): + """ + A bank template manifest for import/export. Version field enables forward-compatible schema evolution: the API auto-upgrades older manifest versions to the current schema on import. + """ # noqa: E501 + version: StrictStr = Field(description="Manifest schema version (currently '1')") + bank: Optional[BankTemplateConfig] = None + mental_models: Optional[List[BankTemplateMentalModel]] = None + directives: Optional[List[BankTemplateDirective]] = None + __properties: ClassVar[List[str]] = ["version", "bank", "mental_models", "directives"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BankTemplateManifest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of bank + if self.bank: + _dict['bank'] = self.bank.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in mental_models (list) + _items = [] + if self.mental_models: + for _item_mental_models in self.mental_models: + if _item_mental_models: + _items.append(_item_mental_models.to_dict()) + _dict['mental_models'] = _items + # override the default output from pydantic by calling `to_dict()` of each item in directives (list) + _items = [] + if self.directives: + for _item_directives in self.directives: + if _item_directives: + _items.append(_item_directives.to_dict()) + _dict['directives'] = _items + # set to None if bank (nullable) is None + # and model_fields_set contains the field + if self.bank is None and "bank" in self.model_fields_set: + _dict['bank'] = None + + # set to None if mental_models (nullable) is None + # and model_fields_set contains the field + if self.mental_models is None and "mental_models" in self.model_fields_set: + _dict['mental_models'] = None + + # set to None if directives (nullable) is None + # and model_fields_set contains the field + if self.directives is None and "directives" in self.model_fields_set: + _dict['directives'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BankTemplateManifest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "version": obj.get("version"), + "bank": BankTemplateConfig.from_dict(obj["bank"]) if obj.get("bank") is not None else None, + "mental_models": [BankTemplateMentalModel.from_dict(_item) for _item in obj["mental_models"]] if obj.get("mental_models") is not None else None, + "directives": [BankTemplateDirective.from_dict(_item) for _item in obj["directives"]] if obj.get("directives") is not None else None + }) + return _obj + + diff --git a/hindsight-clients/python/hindsight_client_api/models/bank_template_mental_model.py b/hindsight-clients/python/hindsight_client_api/models/bank_template_mental_model.py new file mode 100644 index 00000000..a726bac1 --- /dev/null +++ b/hindsight-clients/python/hindsight_client_api/models/bank_template_mental_model.py @@ -0,0 +1,102 @@ +# coding: utf-8 + +""" + Hindsight HTTP API + + HTTP API for Hindsight + + The version of the OpenAPI document: 0.4.22 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from pydantic import BaseModel, ConfigDict, Field, StrictStr +from typing import Any, ClassVar, Dict, List, Optional +from typing_extensions import Annotated +from hindsight_client_api.models.mental_model_trigger_output import MentalModelTriggerOutput +from typing import Optional, Set +from typing_extensions import Self + +class BankTemplateMentalModel(BaseModel): + """ + A mental model definition within a bank template manifest. + """ # noqa: E501 + id: StrictStr = Field(description="Unique ID for the mental model (alphanumeric lowercase with hyphens)") + name: StrictStr = Field(description="Human-readable name for the mental model") + source_query: StrictStr = Field(description="The query to run to generate content") + tags: Optional[List[StrictStr]] = Field(default=None, description="Tags for scoped visibility") + max_tokens: Optional[Annotated[int, Field(le=8192, strict=True, ge=256)]] = Field(default=2048, description="Maximum tokens for generated content") + trigger: Optional[MentalModelTriggerOutput] = Field(default=None, description="Trigger settings") + __properties: ClassVar[List[str]] = ["id", "name", "source_query", "tags", "max_tokens", "trigger"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of BankTemplateMentalModel from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of trigger + if self.trigger: + _dict['trigger'] = self.trigger.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of BankTemplateMentalModel from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "id": obj.get("id"), + "name": obj.get("name"), + "source_query": obj.get("source_query"), + "tags": obj.get("tags"), + "max_tokens": obj.get("max_tokens") if obj.get("max_tokens") is not None else 2048, + "trigger": MentalModelTriggerOutput.from_dict(obj["trigger"]) if obj.get("trigger") is not None else None + }) + return _obj + + diff --git a/hindsight-clients/typescript/generated/sdk.gen.ts b/hindsight-clients/typescript/generated/sdk.gen.ts index 56bd573d..a679323f 100644 --- a/hindsight-clients/typescript/generated/sdk.gen.ts +++ b/hindsight-clients/typescript/generated/sdk.gen.ts @@ -53,6 +53,9 @@ import type { DeleteWebhookData, DeleteWebhookErrors, DeleteWebhookResponses, + ExportBankTemplateData, + ExportBankTemplateErrors, + ExportBankTemplateResponses, FileRetainData, FileRetainErrors, FileRetainResponses, @@ -65,6 +68,8 @@ import type { GetBankProfileData, GetBankProfileErrors, GetBankProfileResponses, + GetBankTemplateSchemaData, + GetBankTemplateSchemaResponses, GetChunkData, GetChunkErrors, GetChunkResponses, @@ -99,6 +104,9 @@ import type { GetVersionResponses, HealthEndpointHealthGetData, HealthEndpointHealthGetResponses, + ImportBankTemplateData, + ImportBankTemplateErrors, + ImportBankTemplateResponses, ListAuditLogsData, ListAuditLogsErrors, ListAuditLogsResponses, @@ -930,6 +938,48 @@ export const createOrUpdateBank = ( }, }); +/** + * Import bank template + * + * Import a bank template manifest to create or update a bank's configuration, mental models, and directives. If the bank does not exist it is created. Config fields are applied as per-bank overrides. Mental models are matched by id, directives by name — existing ones are updated, new ones are created. Use dry_run=true to validate the manifest without applying changes. + */ +export const importBankTemplate = ( + options: Options, +) => + (options.client ?? client).post< + ImportBankTemplateResponses, + ImportBankTemplateErrors, + ThrowOnError + >({ url: "/v1/default/banks/{bank_id}/import", ...options }); + +/** + * Export bank template + * + * Export a bank's current configuration, mental models, and directives as a template manifest. The exported manifest can be imported into another bank to replicate the setup. + */ +export const exportBankTemplate = ( + options: Options, +) => + (options.client ?? client).get< + ExportBankTemplateResponses, + ExportBankTemplateErrors, + ThrowOnError + >({ url: "/v1/default/banks/{bank_id}/export", ...options }); + +/** + * Get bank template JSON Schema + * + * Returns the JSON Schema for the bank template manifest format. Use this to validate template manifests before importing. + */ +export const getBankTemplateSchema = ( + options?: Options, +) => + (options?.client ?? client).get< + GetBankTemplateSchemaResponses, + unknown, + ThrowOnError + >({ url: "/v1/bank-template-schema", ...options }); + /** * Clear all observations * diff --git a/hindsight-clients/typescript/generated/types.gen.ts b/hindsight-clients/typescript/generated/types.gen.ts index e2138dbb..2d9ab0fa 100644 --- a/hindsight-clients/typescript/generated/types.gen.ts +++ b/hindsight-clients/typescript/generated/types.gen.ts @@ -385,6 +385,261 @@ export type BankStatsResponse = { total_observations?: number; }; +/** + * BankTemplateConfig + * + * Bank configuration fields within a template manifest. + * + * Only includes configurable (per-bank) fields. Credential fields + * (API keys, base URLs) are intentionally excluded for security. + */ +export type BankTemplateConfig = { + /** + * Reflect Mission + * + * Mission/context for Reflect operations + */ + reflect_mission?: string | null; + /** + * Retain Mission + * + * Steers what gets extracted during retain + */ + retain_mission?: string | null; + /** + * Retain Extraction Mode + * + * Fact extraction mode: 'concise' (default), 'verbose', or 'custom' + */ + retain_extraction_mode?: string | null; + /** + * Retain Custom Instructions + * + * Custom extraction prompt (when mode='custom') + */ + retain_custom_instructions?: string | null; + /** + * Retain Chunk Size + * + * Max token size for each content chunk + */ + retain_chunk_size?: number | null; + /** + * Enable Observations + * + * Toggle observation consolidation + */ + enable_observations?: boolean | null; + /** + * Observations Mission + * + * Controls what gets synthesised + */ + observations_mission?: string | null; + /** + * Disposition Skepticism + * + * Skepticism trait (1-5) + */ + disposition_skepticism?: number | null; + /** + * Disposition Literalism + * + * Literalism trait (1-5) + */ + disposition_literalism?: number | null; + /** + * Disposition Empathy + * + * Empathy trait (1-5) + */ + disposition_empathy?: number | null; + /** + * Entity Labels + * + * Controlled vocabulary for entity labels + */ + entity_labels?: Array | null; + /** + * Entities Allow Free Form + * + * Allow entities outside the label vocabulary + */ + entities_allow_free_form?: boolean | null; +}; + +/** + * BankTemplateDirective + * + * A directive definition within a bank template manifest. + * + * Directives are matched by name on re-import: existing directives + * with the same name are updated, new ones are created. + */ +export type BankTemplateDirective = { + /** + * Name + * + * Human-readable name for the directive (used as match key on re-import) + */ + name: string; + /** + * Content + * + * The directive text to inject into prompts + */ + content: string; + /** + * Priority + * + * Higher priority directives are injected first + */ + priority?: number; + /** + * Is Active + * + * Whether this directive is active + */ + is_active?: boolean; + /** + * Tags + * + * Tags for filtering + */ + tags?: Array; +}; + +/** + * BankTemplateImportResponse + * + * Response model for the bank template import endpoint. + */ +export type BankTemplateImportResponse = { + /** + * Bank Id + * + * Bank that was imported into + */ + bank_id: string; + /** + * Config Applied + * + * Whether bank config was updated + */ + config_applied: boolean; + /** + * Mental Models Created + * + * IDs of newly created mental models + */ + mental_models_created?: Array; + /** + * Mental Models Updated + * + * IDs of updated mental models + */ + mental_models_updated?: Array; + /** + * Directives Created + * + * Names of newly created directives + */ + directives_created?: Array; + /** + * Directives Updated + * + * Names of updated directives + */ + directives_updated?: Array; + /** + * Operation Ids + * + * Operation IDs for mental model content generation (async) + */ + operation_ids?: Array; + /** + * Dry Run + * + * True if this was a validation-only run + */ + dry_run?: boolean; +}; + +/** + * BankTemplateManifest + * + * A bank template manifest for import/export. + * + * Version field enables forward-compatible schema evolution: the API + * auto-upgrades older manifest versions to the current schema on import. + */ +export type BankTemplateManifest = { + /** + * Version + * + * Manifest schema version (currently '1') + */ + version: string; + /** + * Bank configuration to apply. Omit to leave config unchanged. + */ + bank?: BankTemplateConfig | null; + /** + * Mental Models + * + * Mental models to create or update (matched by id). Omit to leave unchanged. + */ + mental_models?: Array | null; + /** + * Directives + * + * Directives to create or update (matched by name). Omit to leave unchanged. + */ + directives?: Array | null; +}; + +/** + * BankTemplateMentalModel + * + * A mental model definition within a bank template manifest. + */ +export type BankTemplateMentalModel = { + /** + * Id + * + * Unique ID for the mental model (alphanumeric lowercase with hyphens) + */ + id: string; + /** + * Name + * + * Human-readable name for the mental model + */ + name: string; + /** + * Source Query + * + * The query to run to generate content + */ + source_query: string; + /** + * Tags + * + * Tags for scoped visibility + */ + tags?: Array; + /** + * Max Tokens + * + * Maximum tokens for generated content + */ + max_tokens?: number; + /** + * Trigger settings + */ + trigger?: MentalModelTriggerOutput; +}; + /** * Body_file_retain */ @@ -4536,6 +4791,103 @@ export type CreateOrUpdateBankResponses = { export type CreateOrUpdateBankResponse = CreateOrUpdateBankResponses[keyof CreateOrUpdateBankResponses]; +export type ImportBankTemplateData = { + body?: never; + headers?: { + /** + * Authorization + */ + authorization?: string | null; + }; + path: { + /** + * Bank Id + */ + bank_id: string; + }; + query?: { + /** + * Dry Run + * + * Validate only, do not apply changes + */ + dry_run?: boolean; + }; + url: "/v1/default/banks/{bank_id}/import"; +}; + +export type ImportBankTemplateErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ImportBankTemplateError = + ImportBankTemplateErrors[keyof ImportBankTemplateErrors]; + +export type ImportBankTemplateResponses = { + /** + * Successful Response + */ + 200: BankTemplateImportResponse; +}; + +export type ImportBankTemplateResponse = + ImportBankTemplateResponses[keyof ImportBankTemplateResponses]; + +export type ExportBankTemplateData = { + body?: never; + headers?: { + /** + * Authorization + */ + authorization?: string | null; + }; + path: { + /** + * Bank Id + */ + bank_id: string; + }; + query?: never; + url: "/v1/default/banks/{bank_id}/export"; +}; + +export type ExportBankTemplateErrors = { + /** + * Validation Error + */ + 422: HttpValidationError; +}; + +export type ExportBankTemplateError = + ExportBankTemplateErrors[keyof ExportBankTemplateErrors]; + +export type ExportBankTemplateResponses = { + /** + * Successful Response + */ + 200: BankTemplateManifest; +}; + +export type ExportBankTemplateResponse = + ExportBankTemplateResponses[keyof ExportBankTemplateResponses]; + +export type GetBankTemplateSchemaData = { + body?: never; + path?: never; + query?: never; + url: "/v1/bank-template-schema"; +}; + +export type GetBankTemplateSchemaResponses = { + /** + * Successful Response + */ + 200: unknown; +}; + export type ClearObservationsData = { body?: never; headers?: { diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/export/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/export/route.ts new file mode 100644 index 00000000..3e6bdb85 --- /dev/null +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/export/route.ts @@ -0,0 +1,26 @@ +import { NextRequest, NextResponse } from "next/server"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ bankId: string }> } +) { + try { + const { bankId } = await params; + + const url = `${DATAPLANE_URL}/v1/default/banks/${encodeURIComponent(bankId)}/export`; + const response = await fetch(url, { + headers: getDataplaneHeaders(), + }); + + const data = await response.json(); + if (!response.ok) { + return NextResponse.json(data, { status: response.status }); + } + + return NextResponse.json(data, { status: 200 }); + } catch (error) { + console.error("Error exporting bank template:", error); + return NextResponse.json({ error: "Failed to export bank template" }, { status: 500 }); + } +} diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/import/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/import/route.ts new file mode 100644 index 00000000..29c32425 --- /dev/null +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/import/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from "next/server"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; + +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ bankId: string }> } +) { + try { + const { bankId } = await params; + const body = await request.json(); + const dryRun = request.nextUrl.searchParams.get("dry_run") === "true"; + + // Direct fetch since the SDK doesn't have this operation yet + const url = `${DATAPLANE_URL}/v1/default/banks/${encodeURIComponent(bankId)}/import${dryRun ? "?dry_run=true" : ""}`; + const response = await fetch(url, { + method: "POST", + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), + body: JSON.stringify(body), + }); + + const data = await response.json(); + if (!response.ok) { + return NextResponse.json(data, { status: response.status }); + } + + return NextResponse.json(data, { status: 200 }); + } catch (error) { + console.error("Error importing bank template:", error); + return NextResponse.json({ error: "Failed to import bank template" }, { status: 500 }); + } +} diff --git a/hindsight-control-plane/src/app/banks/[bankId]/page.tsx b/hindsight-control-plane/src/app/banks/[bankId]/page.tsx index 81767b5e..7e9eafbd 100644 --- a/hindsight-control-plane/src/app/banks/[bankId]/page.tsx +++ b/hindsight-control-plane/src/app/banks/[bankId]/page.tsx @@ -38,7 +38,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { Brain, Trash2, Loader2, MoreVertical, Pencil, RotateCcw } from "lucide-react"; +import { Brain, Download, Trash2, Loader2, MoreVertical, Pencil, RotateCcw } from "lucide-react"; type NavItem = "recall" | "reflect" | "data" | "documents" | "entities" | "profile"; type DataSubTab = "world" | "experience" | "observations" | "mental-models"; @@ -182,6 +182,23 @@ export default function BankPage() { + { + if (!bankId) return; + try { + const manifest = await client.exportBankTemplate(bankId); + const json = JSON.stringify(manifest, null, 2); + await navigator.clipboard.writeText(json); + toast.success("Template copied to clipboard"); + } catch { + toast.error("Failed to export template"); + } + }} + > + + Export Template + + (null); + const [useTemplate, setUseTemplate] = React.useState(false); + const [templateJson, setTemplateJson] = React.useState(""); + const [templateError, setTemplateError] = React.useState(null); // Document creation state const [docDialogOpen, setDocDialogOpen] = React.useState(false); @@ -137,12 +141,39 @@ function BankSelectorInner() { setIsCreating(true); setCreateError(null); + setTemplateError(null); try { + // Create the bank first await client.createBank(newBankId.trim()); + + // If template JSON is provided, import it + if (templateJson.trim()) { + let manifest: Record; + try { + manifest = JSON.parse(templateJson.trim()); + } catch { + setTemplateError("Invalid JSON. Please check the template syntax."); + setIsCreating(false); + return; + } + + try { + await client.importBankTemplate(newBankId.trim(), manifest); + } catch (importError) { + setTemplateError( + importError instanceof Error ? importError.message : "Failed to import template" + ); + setIsCreating(false); + return; + } + } + await loadBanks(); setCreateDialogOpen(false); setNewBankId(""); + setTemplateJson(""); + setTemplateError(null); // Navigate to the new bank setCurrentBank(newBankId.trim()); router.push(`/banks/${newBankId.trim()}?view=data`); @@ -475,6 +506,7 @@ function BankSelectorInner() { className="h-9 gap-1.5" onClick={() => setDocDialogOpen(true)} title="Add document to current bank" + data-add-document > Add Document @@ -511,23 +543,68 @@ function BankSelectorInner() { - + Create New Memory Bank -
+
setNewBankId(e.target.value)} onKeyDown={(e) => { - if (e.key === "Enter" && !isCreating) { + if (e.key === "Enter" && !isCreating && !useTemplate) { handleCreateBank(); } }} autoFocus /> - {createError &&

{createError}

} +
+
+ { + setUseTemplate(checked); + if (!checked) { + setTemplateJson(""); + setTemplateError(null); + } + }} + /> + +
+ {useTemplate && ( + + Browse templates → + + )} +
+ {useTemplate && ( +
+

+ Paste a template manifest JSON to pre-configure the bank with settings, mental + models, and directives. +

+