From 43f59a85c6d7c5d7f2da557ebb1f0ae2cba78cea Mon Sep 17 00:00:00 2001 From: Ran <16112591+chen-ran@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:36:45 +0800 Subject: [PATCH 1/3] refactor(memory): remove sparse vector subsystem and clean up dead code Delete the entire sparse-vector memory path (sparse runtime, Flask encoder service, Dockerfile.sparse, Qdrant sparse methods) and the dead code it left behind. Sparse retrieval is superseded by the upcoming PG-backed wiki graph, where Qdrant becomes an optional auxiliary seed index rather than a primary store. Sparse removal: - Drop ModeSparse, the sparseRuntime, internal/memory/sparse/ (encoder + Flask service), docker/Dockerfile.sparse, and the Qdrant sparse methods (EnsureCollection, Upsert, Search, SparseVector type, strPtr). - Remove [sparse] from all 9 config TOMLs, the sparse service + NO_PROXY tokens from every docker-compose file, the CI matrix entries, the USE_SPARSE handling in scripts/install.sh, and the AGENTS/DEPLOYMENT/ CONTRIBUTING docs. - Strip sparse from the web UI: builtin-config mode list, settings-context-card status logic, and the sparseSectionTitle/sparseInstallHint/... i18n keys (en/zh/ja). Drop SparseConfig from packages/config types. - Migrate builtin/formation/file tests off the deleted sparse fakes onto a new shared in-memory fakeStore. Incidental cleanup exposed by the deletion: - Remove vestigial adapters types with no callers: EmbedInput, EmbedUpsertRequest, EmbedUpsertResponse, MemoryCompactCapability.Native. - Relocate runtimeHash from dense_runtime.go into shared.go next to its sibling shared helpers (it was the last "shared" helper stranded in the dense file). - Consolidate the two duplicated parseQdrantHostPort implementations into a single qdrant.ParseHostPort, used by both the factory and the status service. - Fix a latent parallel-test race in runtimeMemoryID by appending a process-wide monotonic counter so two Adds in the same nanosecond no longer collide on their ID. Regenerate swagger + TS SDK (TopKBucket/CDFCurve and the removed types drop out of the OpenAPI schema). --- .github/workflows/docker.yml | 15 +- AGENTS.md | 3 +- CONTRIBUTING.md | 2 +- DEPLOYMENT.md | 11 +- apps/web/src/i18n/locales/en.json | 8 - apps/web/src/i18n/locales/ja.json | 7 - apps/web/src/i18n/locales/zh.json | 8 - .../bots/components/settings-context-card.vue | 16 +- .../memory/components/builtin-config.vue | 12 +- conf/app.apple.toml | 3 - conf/app.docker.toml | 3 - conf/app.example.toml | 3 - conf/app.kata.docker.toml | 3 - conf/app.local.toml | 3 - conf/app.windows.toml | 3 - devenv/app.dev.toml | 3 - devenv/app.kata.dev.toml | 3 - devenv/app.sqlite.dev.toml | 3 - devenv/docker-compose.minify.yml | 19 +- devenv/docker-compose.sqlite.minify.yml | 19 +- devenv/docker-compose.sqlite.yml | 18 +- devenv/docker-compose.yml | 23 +- docker-compose.sqlite.yml | 20 +- docker-compose.yml | 24 +- docker/Dockerfile.sparse | 43 -- docker/docker-compose.cn.yml | 2 - docker/docker-compose.sqlite.cn.yml | 2 - docker/docker-compose.yml | 6 +- internal/config/config.go | 5 - internal/memory/adapters/builtin/builtin.go | 2 +- .../memory/adapters/builtin/builtin_test.go | 66 +- .../memory/adapters/builtin/dense_runtime.go | 11 +- internal/memory/adapters/builtin/factory.go | 57 +- .../adapters/builtin/file_runtime_test.go | 4 +- .../memory/adapters/builtin/formation_test.go | 66 +- internal/memory/adapters/builtin/shared.go | 21 +- .../memory/adapters/builtin/sparse_runtime.go | 618 ------------------ .../adapters/builtin/sparse_runtime_test.go | 419 ------------ .../memory/adapters/builtin/store_test.go | 77 +++ internal/memory/adapters/service.go | 37 +- internal/memory/adapters/types.go | 61 +- internal/memory/qdrant/client.go | 119 +--- internal/memory/sparse/encoder.go | 159 ----- internal/memory/sparse/service/main.py | 150 ----- .../memory/sparse/service/requirements.txt | 6 - packages/config/src/types.ts | 5 - scripts/install.sh | 29 +- 47 files changed, 220 insertions(+), 1977 deletions(-) delete mode 100644 docker/Dockerfile.sparse delete mode 100644 internal/memory/adapters/builtin/sparse_runtime.go delete mode 100644 internal/memory/adapters/builtin/sparse_runtime_test.go create mode 100644 internal/memory/adapters/builtin/store_test.go delete mode 100644 internal/memory/sparse/encoder.go delete mode 100644 internal/memory/sparse/service/main.py delete mode 100644 internal/memory/sparse/service/requirements.txt mode change 100755 => 100644 scripts/install.sh diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c68ab1e27f..d3a873fc7d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -36,7 +36,6 @@ jobs: outputs: server: ${{ steps.filter.outputs.server }} web: ${{ steps.filter.outputs.web }} - sparse: ${{ steps.filter.outputs.sparse }} workspace: ${{ steps.filter.outputs.workspace }} steps: - uses: actions/checkout@v4 @@ -67,9 +66,6 @@ jobs: - 'pnpm-workspace.yaml' - 'docker/Dockerfile.web' - 'docker/nginx.conf' - sparse: - - 'internal/memory/sparse/service/**' - - 'docker/Dockerfile.sparse' workspace: - 'docker/Dockerfile.workspace' - 'scripts/desktop-install.sh' @@ -79,7 +75,7 @@ jobs: strategy: fail-fast: false matrix: - image: [server, web, sparse, workspace] + image: [server, web, workspace] platform: [linux/amd64, linux/arm64] variant: ["", debian] exclude: @@ -87,8 +83,6 @@ jobs: variant: debian - image: web variant: debian - - image: sparse - variant: debian - image: workspace variant: "" include: @@ -96,8 +90,6 @@ jobs: dockerfile: docker/Dockerfile.server - image: web dockerfile: docker/Dockerfile.web - - image: sparse - dockerfile: docker/Dockerfile.sparse - image: workspace dockerfile: docker/Dockerfile.workspace - platform: linux/amd64 @@ -120,7 +112,6 @@ jobs: case "$IMAGE" in server) CHANGED="${{ needs.detect-changes.outputs.server }}" ;; web) CHANGED="${{ needs.detect-changes.outputs.web }}" ;; - sparse) CHANGED="${{ needs.detect-changes.outputs.sparse }}" ;; workspace) CHANGED="${{ needs.detect-changes.outputs.workspace }}" ;; esac if [[ "$FORCE" == "true" || "$CHANGED" == "true" ]]; then @@ -230,9 +221,6 @@ jobs: - image: web artifact_key: web variant: "" - - image: sparse - artifact_key: sparse - variant: "" - image: workspace artifact_key: workspace-debian variant: debian @@ -251,7 +239,6 @@ jobs: case "$IMAGE" in server) CHANGED="${{ needs.detect-changes.outputs.server }}" ;; web) CHANGED="${{ needs.detect-changes.outputs.web }}" ;; - sparse) CHANGED="${{ needs.detect-changes.outputs.sparse }}" ;; workspace) CHANGED="${{ needs.detect-changes.outputs.workspace }}" ;; esac if [[ "$FORCE" == "true" || "$CHANGED" == "true" ]]; then diff --git a/AGENTS.md b/AGENTS.md index 22bf6b4c05..0a31d017b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -284,7 +284,7 @@ docker compose up -d # Start all services ``` Production deploy services are `postgres`, `migrate`, `server`, and `web`. -Optional profiles: `qdrant` (vector DB), `sparse` (BM25 search). This is distinct from the native desktop client, which manages its own local server and embedded Qdrant instead of using the Compose web/server split. +Optional profiles: `qdrant` (vector DB). This is distinct from the native desktop client, which manages its own local server and embedded Qdrant instead of using the Compose web/server split. ## Key Development Rules @@ -458,7 +458,6 @@ The main configuration file is `config.toml` (copied from `conf/app.example.toml - `[postgres]` — PostgreSQL connection - `[sqlite]` — SQLite database file and WAL/lock settings - `[qdrant]` — Qdrant vector database connection -- `[sparse]` — Sparse (BM25) search service connection - `[web]` — Web frontend address - `[registry]` — Provider registry (`providers_dir` pointing to `conf/providers/`) - `[supermarket]` — Supermarket integration (base_url) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 45b0c0adc1..06fb92d49e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ mise run dev # Start the full containerized dev environment 4. Web frontend with Vite hot reload The dev stack uses `devenv/app.dev.toml` directly and does not overwrite the repo root `config.toml`. -Default host ports are shifted away from the production compose stack: Web `18082`, API `18080`, Postgres `15432`, Qdrant `16333`/`16334`, Sparse `18085`. +Default host ports are shifted away from the production compose stack: Web `18082`, API `18080`, Postgres `15432`, Qdrant `16333`/`16334`. ## Daily Development diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 0cb2e07ade..b4ec73f773 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -30,10 +30,10 @@ nano config.toml # Change passwords and JWT secret > **Important**: You must create `config.toml` before starting. `docker-compose.yml` mounts `./config.toml` into the containers — running without it will fail. -### Standard startup (with Qdrant + Sparse) +### Standard startup (with Qdrant) ```bash -docker compose --profile qdrant --profile sparse up -d +docker compose --profile qdrant up -d ``` ### Minimal startup (core only) @@ -81,13 +81,12 @@ The base `docker-compose.yml` contains all services. Core services (`postgres`, | Profile | Service | Description | |---------|---------|-------------| | `qdrant` | Qdrant | Vector database for memory semantic search | -| `sparse` | Sparse | Neural sparse memory retrieval service | ### Supported combinations ```bash -# Core + Qdrant + Sparse (recommended default) -docker compose --profile qdrant --profile sparse up -d +# Core + Qdrant (recommended default) +docker compose --profile qdrant up -d ``` ### SaaS / external providers @@ -100,7 +99,7 @@ Uncomment `registry = "memoh.cn"` in `config.toml` under `[container]`, then add ```bash docker compose -f docker-compose.yml -f docker/docker-compose.cn.yml \ - --profile qdrant --profile sparse up -d + --profile qdrant up -d ``` ## Prerequisites diff --git a/apps/web/src/i18n/locales/en.json b/apps/web/src/i18n/locales/en.json index 61e322f5f3..37a67ba6c3 100644 --- a/apps/web/src/i18n/locales/en.json +++ b/apps/web/src/i18n/locales/en.json @@ -994,16 +994,13 @@ "modeHint": "How bots store and recall long-term memory.", "advanced": "Advanced", "advancedHint": "Custom memory backends for advanced setups.", - "sparseSectionTitle": "Sparse Retrieval", "denseSectionTitle": "Dense Retrieval", - "sparseInstallHint": "Sparse mode depends on the optional sparse service. Enable the sparse installation option when running the installer or start the sparse profile in Docker Compose.", "denseBackend": "Dense Backend", "denseBackendValue": "Embedding API + Qdrant", "denseEmbeddingModel": "Dense Embedding Model", "denseEmbeddingModelDescription": "Select the third-party embedding model used before local rerank.", "denseQdrantHint": "Dense memory will use Qdrant as the storage backend after the backend runtime is connected.", "qdrantCollection": "Qdrant Collection", - "sparseQdrantCollectionDescription": "Sparse mode writes to the Qdrant collection. Current/default collection: {collection}.", "denseQdrantCollectionDescription": "Dense mode writes to the Qdrant collection. Current/default collection: {collection}.", "collectionPoints": "Points in collection", "collectionExists": "Collection exists", @@ -1012,12 +1009,10 @@ "collectionUnavailable": "Unavailable", "modeNames": { "off": "Off", - "sparse": "Keyword", "dense": "Semantic" }, "modeDescriptions": { "off": "Don't build a long-term memory index; keep only the basic file memory.", - "sparse": "Match memories by keyword, using the optional sparse retrieval service.", "dense": "Turn memories into vectors with an embedding model and retrieve by semantic similarity." }, "providerNames": { @@ -2004,8 +1999,6 @@ "memoryProvider": "Memory Provider", "memoryProviderPlaceholder": "Select memory provider (disabled if empty)", "memoryModePreview": "Selected built-in mode: {mode}", - "sparseStatusTitle": "Sparse Retrieval Status", - "sparseStatusHint": "Markdown files are the source of truth. Syncing rebuilds the Qdrant index from /data/memory.", "denseStatusTitle": "Dense Retrieval Status", "denseStatusHint": "Markdown files are the source of truth. Syncing rebuilds the dense Qdrant index from /data/memory.", "mem0StatusTitle": "Mem0 Sync Status", @@ -2020,7 +2013,6 @@ "memorySourceEntries": "Source Entries", "memoryIndexedEntries": "Indexed Entries", "memoryQdrantCollection": "Qdrant Collection", - "memoryEncoderHealth": "Sparse Encoder", "memoryDenseEmbeddingHealth": "Embedding Backend", "memoryQdrantHealth": "Qdrant", "memoryHealthOk": "Healthy", diff --git a/apps/web/src/i18n/locales/ja.json b/apps/web/src/i18n/locales/ja.json index 14d94bf57d..f2999d6a6d 100644 --- a/apps/web/src/i18n/locales/ja.json +++ b/apps/web/src/i18n/locales/ja.json @@ -941,16 +941,13 @@ "modeHint": "Botが長期記憶を保存し、呼び出す方法。", "advanced": "詳細", "advancedHint": "高度な設定向けのカスタムメモリバックエンド。", - "sparseSectionTitle": "スパース検索", "denseSectionTitle": "密な検索", - "sparseInstallHint": "スパース モードは、オプションのスパース サービスに依存します。インストーラーの実行時にスパース インストール オプションを有効にするか、Docker Compose でスパース プロファイルを開始します。", "denseBackend": "Dense バックエンド", "denseBackendValue": "埋め込みAPI+ Qdrant", "denseEmbeddingModel": "Dense 埋め込みモデル", "denseEmbeddingModelDescription": "ローカルの再ランク付け前に使用するサードパーティの埋め込みモデルを選択します。", "denseQdrantHint": "バックエンドランタイムの接続後、Dense メモリは Qdrant をストレージバックエンドとして使用します。", "qdrantCollection": "Qdrant コレクション", - "sparseQdrantCollectionDescription": "スパース モードは Qdrant コレクションに書き込みます。現在/デフォルトのコレクション: {collection}。", "denseQdrantCollectionDescription": "デンス モードは Qdrant コレクションに書き込みます。現在/デフォルトのコレクション: {collection}。", "collectionPoints": "コレクション内のポイント数", "collectionExists": "コレクションが存在します", @@ -959,12 +956,10 @@ "collectionUnavailable": "利用不可", "modeNames": { "off": "オフ", - "sparse": "キーワード", "dense": "セマンティック" }, "modeDescriptions": { "off": "長期記憶インデックスを作成せず、基本的なファイルメモリのみを保持します。", - "sparse": "オプションのスパース検索サービスを使用して、キーワードで記憶を照合します。", "dense": "埋め込みモデルを使用して記憶をベクトルに変換し、意味の類似性で検索します。" }, "providerNames": { @@ -1985,8 +1980,6 @@ "memoryProvider": "MemoryProvider", "memoryProviderPlaceholder": "MemoryProviderの選択 (空の場合は無効)", "memoryModePreview": "選択された組み込みモード: {mode}", - "sparseStatusTitle": "スパース取得ステータス", - "sparseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から Qdrant インデックスが再構築されます。", "denseStatusTitle": "密集検索ステータス", "denseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から高密度 Qdrant インデックスが再構築されます。", "mem0StatusTitle": "Mem0 同期ステータス", diff --git a/apps/web/src/i18n/locales/zh.json b/apps/web/src/i18n/locales/zh.json index 2902a32a96..84b49767e0 100644 --- a/apps/web/src/i18n/locales/zh.json +++ b/apps/web/src/i18n/locales/zh.json @@ -994,16 +994,13 @@ "modeHint": "决定智能体如何存取长期记忆。", "advanced": "高级", "advancedHint": "面向进阶场景的自定义记忆后端。", - "sparseSectionTitle": "稀疏检索", "denseSectionTitle": "稠密检索", - "sparseInstallHint": "Sparse 模式依赖可选的 sparse 服务。安装时请启用 sparse 选项,或在 Docker Compose 中启动 sparse profile。", "denseBackend": "Dense 后端", "denseBackendValue": "Embedding API + Qdrant", "denseEmbeddingModel": "Dense 向量模型", "denseEmbeddingModelDescription": "选择本地 rerank 之前使用的第三方 embedding 模型。", "denseQdrantHint": "后端接通后,dense memory 会以 Qdrant 作为存储后端。", "qdrantCollection": "Qdrant Collection", - "sparseQdrantCollectionDescription": "稀疏模式会写入对应的 Qdrant collection。当前/默认 collection:{collection}。", "denseQdrantCollectionDescription": "稠密模式会写入对应的 Qdrant collection。当前/默认 collection:{collection}。", "collectionPoints": "Collection 中的点数", "collectionExists": "Collection 已存在", @@ -1012,12 +1009,10 @@ "collectionUnavailable": "不可用", "modeNames": { "off": "关闭", - "sparse": "关键词", "dense": "语义" }, "modeDescriptions": { "off": "不建立长期记忆索引,仅保留基础的文件记忆。", - "sparse": "用关键词匹配检索记忆,依赖可选的稀疏检索服务。", "dense": "用向量模型把记忆转成向量,按语义相近度检索。" }, "providerNames": { @@ -2004,8 +1999,6 @@ "memoryProvider": "记忆提供方", "memoryProviderPlaceholder": "选择记忆提供方(为空则禁用)", "memoryModePreview": "当前内置模式:{mode}", - "sparseStatusTitle": "稀疏检索状态", - "sparseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重新构建 Qdrant 索引。", "denseStatusTitle": "稠密检索状态", "denseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重建稠密 Qdrant 索引。", "mem0StatusTitle": "Mem0 同步状态", @@ -2020,7 +2013,6 @@ "memorySourceEntries": "源条目数", "memoryIndexedEntries": "索引条目数", "memoryQdrantCollection": "Qdrant Collection", - "memoryEncoderHealth": "Sparse Encoder", "memoryDenseEmbeddingHealth": "Embedding 后端", "memoryQdrantHealth": "Qdrant", "memoryHealthOk": "正常", diff --git a/apps/web/src/pages/bots/components/settings-context-card.vue b/apps/web/src/pages/bots/components/settings-context-card.vue index 3de42922cc..423ff536a2 100644 --- a/apps/web/src/pages/bots/components/settings-context-card.vue +++ b/apps/web/src/pages/bots/components/settings-context-card.vue @@ -248,7 +248,7 @@ const isSelectedMemoryProviderPersisted = computed(() => !!props.form.memory_provider_id && props.form.memory_provider_id === props.persistedMemoryProviderID, ) const showBuiltinIndexedMemoryStatus = computed(() => - selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense', + selectedBuiltinMemoryMode.value === 'dense', ) const showMemoryProviderStatusCard = computed(() => showBuiltinIndexedMemoryStatus.value || !!selectedMem0MemoryProvider.value, @@ -257,25 +257,21 @@ const showMemoryProviderStatusCard = computed(() => const indexedMemoryStatusTitle = computed(() => selectedMemoryProviderType.value === 'mem0' ? t('bots.settings.mem0StatusTitle') - : selectedBuiltinMemoryMode.value === 'dense' - ? t('bots.settings.denseStatusTitle') - : t('bots.settings.sparseStatusTitle'), + : t('bots.settings.denseStatusTitle'), ) const statusCardData = computed(() => props.memoryStatus) const showQdrantDetails = computed(() => - selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense', + selectedBuiltinMemoryMode.value === 'dense', ) const showEncoderHealth = computed(() => - selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense', + selectedBuiltinMemoryMode.value === 'dense', ) const showQdrantHealth = computed(() => - selectedBuiltinMemoryMode.value === 'sparse' || selectedBuiltinMemoryMode.value === 'dense', + selectedBuiltinMemoryMode.value === 'dense', ) const encoderHealthLabel = computed(() => - selectedBuiltinMemoryMode.value === 'dense' - ? t('bots.settings.memoryDenseEmbeddingHealth') - : t('bots.settings.memoryEncoderHealth'), + t('bots.settings.memoryDenseEmbeddingHealth'), ) function healthTextClass(ok: boolean | undefined) { diff --git a/apps/web/src/pages/memory/components/builtin-config.vue b/apps/web/src/pages/memory/components/builtin-config.vue index 8c604e60a3..fcb4291edf 100644 --- a/apps/web/src/pages/memory/components/builtin-config.vue +++ b/apps/web/src/pages/memory/components/builtin-config.vue @@ -23,13 +23,6 @@ {{ $t(`memory.modeDescriptions.${mode}`) }}

-
- {{ $t('memory.sparseInstallHint') }} -
-
(statusData.value as AdaptersProviderStatusRe const modeItems = computed[]>(() => [ { value: 'off', label: t('memory.modeNames.off') }, - { value: 'sparse', label: t('memory.modeNames.sparse') }, { value: 'dense', label: t('memory.modeNames.dense') }, ]) watch(() => props.provider, (val) => { const config = (val?.config ?? {}) as Record const nextMode = config.memory_mode - mode.value = nextMode === 'sparse' || nextMode === 'dense' ? nextMode : 'off' + mode.value = nextMode === 'dense' ? 'dense' : 'off' embeddingModelId.value = typeof config.embedding_model_id === 'string' ? config.embedding_model_id : '' }, { immediate: true }) diff --git a/conf/app.apple.toml b/conf/app.apple.toml index 7df8355d9f..2c18a9fee9 100644 --- a/conf/app.apple.toml +++ b/conf/app.apple.toml @@ -60,9 +60,6 @@ base_url = "http://127.0.0.1:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://127.0.0.1:8085" - [registry] providers_dir = "conf/providers" diff --git a/conf/app.docker.toml b/conf/app.docker.toml index 7b6051e9cc..b759101aef 100644 --- a/conf/app.docker.toml +++ b/conf/app.docker.toml @@ -85,9 +85,6 @@ base_url = "http://qdrant:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://sparse:8085" - [registry] providers_dir = "conf/providers" diff --git a/conf/app.example.toml b/conf/app.example.toml index 23e2eaeae4..2b4c336cff 100644 --- a/conf/app.example.toml +++ b/conf/app.example.toml @@ -132,9 +132,6 @@ base_url = "http://127.0.0.1:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://127.0.0.1:8085" - [registry] providers_dir = "conf/providers" diff --git a/conf/app.kata.docker.toml b/conf/app.kata.docker.toml index ce4fa27f39..b75b212e80 100644 --- a/conf/app.kata.docker.toml +++ b/conf/app.kata.docker.toml @@ -86,9 +86,6 @@ base_url = "http://qdrant:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://sparse:8085" - [registry] providers_dir = "conf/providers" diff --git a/conf/app.local.toml b/conf/app.local.toml index 04f2f83c58..837538acf3 100644 --- a/conf/app.local.toml +++ b/conf/app.local.toml @@ -71,9 +71,6 @@ base_url = "http://127.0.0.1:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://127.0.0.1:8085" - [registry] providers_dir = "conf/providers" diff --git a/conf/app.windows.toml b/conf/app.windows.toml index 780af7f2b7..b32ffbf250 100644 --- a/conf/app.windows.toml +++ b/conf/app.windows.toml @@ -68,9 +68,6 @@ base_url = "http://127.0.0.1:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://127.0.0.1:8085" - [registry] providers_dir = "conf/providers" diff --git a/devenv/app.dev.toml b/devenv/app.dev.toml index 55b8b2d5f9..df9b424e05 100644 --- a/devenv/app.dev.toml +++ b/devenv/app.dev.toml @@ -70,9 +70,6 @@ base_url = "http://qdrant:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://sparse:8085" - [registry] providers_dir = "conf/providers" diff --git a/devenv/app.kata.dev.toml b/devenv/app.kata.dev.toml index 1251c1ccfb..9b38084bb9 100644 --- a/devenv/app.kata.dev.toml +++ b/devenv/app.kata.dev.toml @@ -59,9 +59,6 @@ base_url = "http://qdrant:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://sparse:8085" - [registry] providers_dir = "conf/providers" diff --git a/devenv/app.sqlite.dev.toml b/devenv/app.sqlite.dev.toml index 3e9285d6fe..fb5fb813b4 100644 --- a/devenv/app.sqlite.dev.toml +++ b/devenv/app.sqlite.dev.toml @@ -71,9 +71,6 @@ base_url = "http://qdrant:6334" api_key = "" timeout_seconds = 10 -[sparse] -base_url = "http://sparse:8085" - [web] host = "0.0.0.0" port = 8082 diff --git a/devenv/docker-compose.minify.yml b/devenv/docker-compose.minify.yml index 9864fcae5c..426be7b9df 100644 --- a/devenv/docker-compose.minify.yml +++ b/devenv/docker-compose.minify.yml @@ -90,10 +90,10 @@ services: # HTTP_PROXY=http://host.docker.internal:7890 mise run dev HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}" volumes: @@ -146,21 +146,6 @@ services: condition: service_healthy restart: unless-stopped - # sparse: - # build: - # context: .. - # dockerfile: docker/Dockerfile.sparse - # container_name: memoh-dev-sparse - # ports: - # - "${MEMOH_DEV_SPARSE_PORT:-18085}:8085" - # healthcheck: - # test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - # interval: 15s - # timeout: 10s - # start_period: 30s - # retries: 3 - # restart: unless-stopped - volumes: postgres_data: driver: local diff --git a/devenv/docker-compose.sqlite.minify.yml b/devenv/docker-compose.sqlite.minify.yml index 06d848c8d1..d09e438c5a 100644 --- a/devenv/docker-compose.sqlite.minify.yml +++ b/devenv/docker-compose.sqlite.minify.yml @@ -70,10 +70,10 @@ services: # HTTP_PROXY=http://host.docker.internal:7890 mise run dev:sqlite:minify HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30200}" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30300}" volumes: @@ -126,21 +126,6 @@ services: condition: service_healthy restart: unless-stopped - # sparse: - # build: - # context: .. - # dockerfile: docker/Dockerfile.sparse - # container_name: memoh-dev-sqlite-sparse - # ports: - # - "${MEMOH_SQLITE_DEV_SPARSE_PORT:-19085}:8085" - # healthcheck: - # test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - # interval: 15s - # timeout: 10s - # start_period: 30s - # retries: 3 - # restart: unless-stopped - volumes: qdrant_data: driver: local diff --git a/devenv/docker-compose.sqlite.yml b/devenv/docker-compose.sqlite.yml index d0053cb2bd..9c342b8934 100644 --- a/devenv/docker-compose.sqlite.yml +++ b/devenv/docker-compose.sqlite.yml @@ -70,10 +70,10 @@ services: # HTTP_PROXY=http://host.docker.internal:7890 mise run dev:sqlite HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,sparse,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-sparse,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},qdrant,server,web,migrate,deps,memoh-dev-sqlite-qdrant,memoh-dev-sqlite-server,memoh-dev-sqlite-web,memoh-dev-sqlite-migrate,memoh-dev-sqlite-deps" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30200}" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_SQLITE_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30300}" volumes: @@ -129,20 +129,6 @@ services: condition: service_healthy restart: unless-stopped - sparse: - build: - context: .. - dockerfile: docker/Dockerfile.sparse - container_name: memoh-dev-sqlite-sparse - ports: - - "${MEMOH_SQLITE_DEV_SPARSE_PORT:-19085}:8085" - healthcheck: - test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - interval: 15s - timeout: 10s - start_period: 30s - retries: 3 - restart: unless-stopped volumes: qdrant_data: diff --git a/devenv/docker-compose.yml b/devenv/docker-compose.yml index baedfdde7c..a8078cda61 100644 --- a/devenv/docker-compose.yml +++ b/devenv/docker-compose.yml @@ -65,10 +65,10 @@ services: GOFLAGS: -buildvcs=false HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" volumes: - ..:/workspace - go_mod_cache:/go/pkg/mod @@ -98,10 +98,10 @@ services: # HTTP_PROXY=http://host.docker.internal:7890 mise run dev HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-sparse,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,deps,memoh-dev-postgres,memoh-dev-qdrant,memoh-dev-server,memoh-dev-web,memoh-dev-migrate,memoh-dev-deps" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DEV_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}" volumes: @@ -157,21 +157,6 @@ services: condition: service_healthy restart: unless-stopped - sparse: - build: - context: .. - dockerfile: docker/Dockerfile.sparse - container_name: memoh-dev-sparse - ports: - - "${MEMOH_DEV_SPARSE_PORT:-18085}:8085" - healthcheck: - test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - interval: 15s - timeout: 10s - start_period: 30s - retries: 3 - restart: unless-stopped - volumes: postgres_data: driver: local diff --git a/docker-compose.sqlite.yml b/docker-compose.sqlite.yml index 473a07f116..69ac15bc3e 100644 --- a/docker-compose.sqlite.yml +++ b/docker-compose.sqlite.yml @@ -47,28 +47,10 @@ services: networks: - memoh-network - sparse: - image: memohai/sparse:latest - container_name: memoh-sparse - profiles: [sparse] - volumes: - - /etc/localtime:/etc/localtime:ro - expose: - - "8085" - healthcheck: - test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - interval: 15s - timeout: 10s - start_period: 30s - retries: 3 - restart: unless-stopped - networks: - - memoh-network - qdrant: image: qdrant/qdrant:latest container_name: memoh-qdrant - profiles: [qdrant, sparse] + profiles: [qdrant] volumes: - qdrant_data:/qdrant/storage expose: diff --git a/docker-compose.yml b/docker-compose.yml index 893aa6fd81..974bc33bbd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,10 +49,10 @@ services: # HTTP_PROXY=http://host.docker.internal:7890 docker compose up -d HTTP_PROXY: "${HTTP_PROXY:-${http_proxy:-}}" HTTPS_PROXY: "${HTTPS_PROXY:-${https_proxy:-}}" - NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,sparse,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-sparse,memoh-server,memoh-web,memoh-migrate" + NO_PROXY: "${NO_PROXY:-127.0.0.1,localhost},postgres,qdrant,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-server,memoh-web,memoh-migrate" http_proxy: "${http_proxy:-${HTTP_PROXY:-}}" https_proxy: "${https_proxy:-${HTTPS_PROXY:-}}" - no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,sparse,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-sparse,memoh-server,memoh-web,memoh-migrate" + no_proxy: "${no_proxy:-${NO_PROXY:-127.0.0.1,localhost}},postgres,qdrant,server,web,migrate,memoh-postgres,memoh-qdrant,memoh-server,memoh-web,memoh-migrate" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN: "${MEMOH_DISPLAY_WEBRTC_UDP_PORT_MIN:-30000}" MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX: "${MEMOH_DISPLAY_WEBRTC_UDP_PORT_MAX:-30100}" volumes: @@ -89,28 +89,10 @@ services: networks: - memoh-network - sparse: - image: memohai/sparse:latest - container_name: memoh-sparse - profiles: [sparse] - volumes: - - /etc/localtime:/etc/localtime:ro - expose: - - "8085" - healthcheck: - test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8085/health')\" || exit 1"] - interval: 15s - timeout: 10s - start_period: 30s - retries: 3 - restart: unless-stopped - networks: - - memoh-network - qdrant: image: qdrant/qdrant:latest container_name: memoh-qdrant - profiles: [qdrant, sparse] + profiles: [qdrant] volumes: - qdrant_data:/qdrant/storage expose: diff --git a/docker/Dockerfile.sparse b/docker/Dockerfile.sparse deleted file mode 100644 index 7fba97e9b2..0000000000 --- a/docker/Dockerfile.sparse +++ /dev/null @@ -1,43 +0,0 @@ -# syntax=docker/dockerfile:1 - -# Sparse encoding service — runs the OpenSearch neural sparse model -# as a standalone HTTP service (Flask + PyTorch CPU). -# Used by the Go server's sparse memory runtime via HTTP. - -FROM python:3.12-slim - -WORKDIR /app - -COPY internal/memory/sparse/service/requirements.txt requirements.txt -RUN pip install --no-cache-dir \ - --extra-index-url https://download.pytorch.org/whl/cpu \ - -r requirements.txt - -COPY internal/memory/sparse/service/main.py main.py - -ENV SPARSE_PORT=8085 -ENV SPARSE_CACHE_DIR=/opt/sparse-cache - -RUN mkdir -p /opt/sparse-cache - -# Pre-download the default sparse model during image build so containers -# start with a warm cache and do not need to fetch weights on first boot. -RUN python - <<'PY' -from pathlib import Path -from huggingface_hub import hf_hub_download -from transformers import AutoModelForMaskedLM, AutoTokenizer - -model_repo = "opensearch-project/opensearch-neural-sparse-encoding-multilingual-v1" -cache_dir = "/opt/sparse-cache" -Path(cache_dir).mkdir(parents=True, exist_ok=True) - -AutoModelForMaskedLM.from_pretrained(model_repo, cache_dir=cache_dir) -AutoTokenizer.from_pretrained(model_repo, cache_dir=cache_dir) -hf_hub_download(repo_id=model_repo, filename="idf.json", cache_dir=cache_dir) - -print(f"Pre-downloaded sparse model: {model_repo}") -PY - -EXPOSE 8085 - -CMD ["python", "main.py"] diff --git a/docker/docker-compose.cn.yml b/docker/docker-compose.cn.yml index 1bbba5ee9a..3bc042fbc2 100644 --- a/docker/docker-compose.cn.yml +++ b/docker/docker-compose.cn.yml @@ -9,5 +9,3 @@ services: image: memoh.cn/memohai/server:latest web: image: memoh.cn/memohai/web:latest - sparse: - image: memoh.cn/memohai/sparse:latest diff --git a/docker/docker-compose.sqlite.cn.yml b/docker/docker-compose.sqlite.cn.yml index dad6564bbc..becc93fa19 100644 --- a/docker/docker-compose.sqlite.cn.yml +++ b/docker/docker-compose.sqlite.cn.yml @@ -7,5 +7,3 @@ services: image: memoh.cn/memohai/server:latest web: image: memoh.cn/memohai/web:latest - sparse: - image: memoh.cn/memohai/sparse:latest diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 637c3e06af..ce01fab319 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,12 +20,8 @@ services: web: build: - context: . + context: .. dockerfile: docker/Dockerfile.web args: - VITE_API_URL=${VITE_API_URL:-/api} - sparse: - build: - context: . - dockerfile: docker/Dockerfile.sparse diff --git a/internal/config/config.go b/internal/config/config.go index 766ee09f00..2bf9db9422 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -58,7 +58,6 @@ type Config struct { Postgres PostgresConfig `toml:"postgres"` SQLite SQLiteConfig `toml:"sqlite"` Qdrant QdrantConfig `toml:"qdrant"` - Sparse SparseConfig `toml:"sparse"` Registry RegistryConfig `toml:"registry"` Supermarket SupermarketConfig `toml:"supermarket"` OAuthClients OAuthClientsConfig `toml:"oauth_clients"` @@ -328,10 +327,6 @@ type QdrantConfig struct { TimeoutSeconds int `toml:"timeout_seconds"` } -type SparseConfig struct { - BaseURL string `toml:"base_url"` -} - const DefaultProvidersDir = "conf/providers" type RegistryConfig struct { diff --git a/internal/memory/adapters/builtin/builtin.go b/internal/memory/adapters/builtin/builtin.go index 8511eed40c..70e595f99d 100644 --- a/internal/memory/adapters/builtin/builtin.go +++ b/internal/memory/adapters/builtin/builtin.go @@ -141,7 +141,7 @@ func (p *BuiltinProvider) SemanticCompactCapability() adapters.MemoryCompactCapa return adapters.MemoryCompactCapability{ Semantic: true, Archive: true, - RebuildIndex: mode == "dense" || mode == "sparse", + RebuildIndex: mode == "dense", } } diff --git a/internal/memory/adapters/builtin/builtin_test.go b/internal/memory/adapters/builtin/builtin_test.go index 9bb16f85ed..47120531fb 100644 --- a/internal/memory/adapters/builtin/builtin_test.go +++ b/internal/memory/adapters/builtin/builtin_test.go @@ -9,7 +9,6 @@ import ( "github.com/memohai/memoh/internal/config" adapters "github.com/memohai/memoh/internal/memory/adapters" - "github.com/memohai/memoh/internal/memory/sparse" storefs "github.com/memohai/memoh/internal/memory/storefs" ) @@ -34,10 +33,8 @@ func TestBuiltinProviderNilService(t *testing.T) { func TestBuiltinProviderSemanticCompactCapability(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) p := NewBuiltinProvider(slog.Default(), runtime, nil, nil) withoutLLM := p.SemanticCompactCapability() @@ -56,8 +53,10 @@ func TestBuiltinProviderSemanticCompactCapability(t *testing.T) { if !withLLM.Archive { t.Fatalf("semantic compact should advertise source archive support: %+v", withLLM) } - if !withLLM.RebuildIndex { - t.Fatalf("semantic compact should advertise index rebuild support for indexed runtime: %+v", withLLM) + // The file runtime (mode "off") has no derived vector index to rebuild, + // so RebuildIndex must be false. Only indexed runtimes (dense) set it. + if withLLM.RebuildIndex { + t.Fatalf("file runtime should not advertise index rebuild support: %+v", withLLM) } if withLLM.Reason != "" { t.Fatalf("available semantic compact should not include unavailable reason: %+v", withLLM) @@ -66,10 +65,8 @@ func TestBuiltinProviderSemanticCompactCapability(t *testing.T) { func TestBuiltinProviderOnBeforeChatEmptyQuery(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) p := NewBuiltinProvider(slog.Default(), runtime, nil, nil) result, err := p.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{ @@ -86,10 +83,8 @@ func TestBuiltinProviderOnBeforeChatEmptyQuery(t *testing.T) { func TestBuiltinProviderContextPackingProducesMemoryContextTags(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) p := NewBuiltinProvider(slog.Default(), runtime, nil, nil) _ = p.OnAfterChat(context.Background(), adapters.AfterChatRequest{ @@ -151,15 +146,13 @@ func TestBuiltinProviderApplyProviderConfigNil(t *testing.T) { func TestBuiltinProviderCompactUsesLLMResults(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore( + store := newFakeStore( storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes black tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_3", Memory: "Ran works in Berlin", CreatedAt: "2026-06-03T00:00:00Z", UpdatedAt: "2026-06-03T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_4", Memory: "Ran uses Vim", CreatedAt: "2026-06-04T00:00:00Z", UpdatedAt: "2026-06-04T00:00:00Z"}, ) - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + runtime := newFileRuntime(store) llm := &fakeLLM{ compactFacts: []string{ "Ran likes tea, especially black tea and oolong.", @@ -216,13 +209,11 @@ func TestBuiltinProviderCompactUsesLLMResults(t *testing.T) { func TestBuiltinProviderCompactRequiresSemanticCompactCapability(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore( + store := newFakeStore( storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes black tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"}, ) - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + runtime := newFileRuntime(store) provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil) if _, err := provider.Compact(context.Background(), map[string]any{"bot_id": "bot-1"}, 0.5, 0); err == nil { @@ -232,15 +223,13 @@ func TestBuiltinProviderCompactRequiresSemanticCompactCapability(t *testing.T) { func TestBuiltinProviderCompactPreservesPinnedMemories(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore( + store := newFakeStore( storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Pinned preference", Metadata: map[string]any{"pinned": true}, CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Read-only profile", Metadata: map[string]any{"read_only": "true"}, CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_3", Memory: "Ran likes green tea", CreatedAt: "2026-06-03T00:00:00Z", UpdatedAt: "2026-06-03T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_4", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-04T00:00:00Z", UpdatedAt: "2026-06-04T00:00:00Z"}, ) - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + runtime := newFileRuntime(store) llm := &fakeLLM{compactFacts: []string{"Ran likes tea."}} provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil) provider.SetLLM(llm) @@ -268,8 +257,6 @@ func TestBuiltinProviderCompactPreservesPinnedMemories(t *testing.T) { func TestBuiltinProviderCompactBatchesOversizedInputs(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) items := make([]storefs.MemoryItem, 0, 24) for i := 0; i < 24; i++ { items = append(items, storefs.MemoryItem{ @@ -279,8 +266,8 @@ func TestBuiltinProviderCompactBatchesOversizedInputs(t *testing.T) { UpdatedAt: "2026-06-01T00:00:00Z", }) } - store := newFakeSparseStore(items...) - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore(items...) + runtime := newFileRuntime(store) llm := &fakeLLM{} llm.compactFunc = func(_ adapters.CompactRequest) adapters.CompactResponse { return adapters.CompactResponse{Facts: []string{ @@ -392,23 +379,6 @@ func TestNewBuiltinRuntimeFromConfig_DenseErrorPropagates(t *testing.T) { } } -func TestNewBuiltinRuntimeFromConfig_SparseErrorPropagates(t *testing.T) { - t.Parallel() - cfg := map[string]any{"memory_mode": "sparse"} - _, err := NewBuiltinRuntimeFromConfig(nil, cfg, nil, nil, defaultTestConfig()) - if err == nil { - t.Fatal("expected error for sparse mode without encoder base URL") - } -} - func defaultTestConfig() config.Config { return config.Config{} } - -// Fakes from sparse_runtime_test.go are in the same package and accessible. - -var _ sparseEncoder = (*fakeSparseEncoder)(nil) - -func init() { - _ = sparse.SparseVector{} -} diff --git a/internal/memory/adapters/builtin/dense_runtime.go b/internal/memory/adapters/builtin/dense_runtime.go index 86ba294c2e..497b5110ef 100644 --- a/internal/memory/adapters/builtin/dense_runtime.go +++ b/internal/memory/adapters/builtin/dense_runtime.go @@ -2,8 +2,6 @@ package builtin import ( "context" - "crypto/sha256" - "encoding/hex" "encoding/json" "errors" "fmt" @@ -65,7 +63,7 @@ func newDenseRuntime(providerConfig map[string]any, queries dbstore.Queries, cfg return nil, err } - host, port := parseQdrantHostPort(cfg.Qdrant.BaseURL) + host, port := qdrantclient.ParseHostPort(cfg.Qdrant.BaseURL) if host == "" { host = "localhost" } @@ -664,10 +662,3 @@ func resolveDenseEmbeddingModel(ctx context.Context, queries dbstore.Queries, mo dimensions: *cfg.Dimensions, }, nil } - -// --- shared helpers (used by both dense and sparse runtimes) --- - -func runtimeHash(text string) string { - sum := sha256.Sum256([]byte(strings.TrimSpace(text))) - return hex.EncodeToString(sum[:]) -} diff --git a/internal/memory/adapters/builtin/factory.go b/internal/memory/adapters/builtin/factory.go index 4352f33cb0..3fd9b5d8e6 100644 --- a/internal/memory/adapters/builtin/factory.go +++ b/internal/memory/adapters/builtin/factory.go @@ -2,7 +2,6 @@ package builtin import ( "log/slog" - "strconv" "strings" "github.com/memohai/memoh/internal/config" @@ -15,41 +14,19 @@ import ( type BuiltinMemoryMode string const ( - ModeOff BuiltinMemoryMode = "off" - ModeSparse BuiltinMemoryMode = "sparse" - ModeDense BuiltinMemoryMode = "dense" + ModeOff BuiltinMemoryMode = "off" + ModeDense BuiltinMemoryMode = "dense" ) // NewBuiltinRuntimeFromConfig returns the appropriate Runtime based on // the provider's persisted config (memory_mode field). Returns the file -// runtime for "off" or unknown modes. Returns an error if a sparse or dense +// runtime for "off" or unknown modes. Returns an error if a dense // runtime was explicitly requested but failed to initialise, so that callers // can surface configuration problems rather than silently degrading. func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, store *storefs.Service, queries dbstore.Queries, cfg config.Config) (Runtime, error) { mode := BuiltinMemoryMode(strings.TrimSpace(adapters.StringFromConfig(providerConfig, "memory_mode"))) switch mode { - case ModeSparse: - host, port := parseQdrantHostPort(cfg.Qdrant.BaseURL) - if host == "" { - host = "localhost" - } - if port == 0 { - port = 6334 - } - collection := adapters.StringFromConfig(providerConfig, "qdrant_collection") - if collection == "" { - collection = "memory_sparse" - } - return newSparseRuntime( - host, - port, - cfg.Qdrant.APIKey, - collection, - strings.TrimSpace(cfg.Sparse.BaseURL), - store, - ) - case ModeDense: return newDenseRuntime(providerConfig, queries, cfg, store) @@ -57,31 +34,3 @@ func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, return NewFileRuntime(store), nil } } - -// parseQdrantHostPort extracts host and gRPC port from a Qdrant base URL. -// Qdrant base URLs are typically HTTP (port 6333), but the gRPC port is 6334. -func parseQdrantHostPort(baseURL string) (string, int) { - baseURL = strings.TrimSpace(baseURL) - if baseURL == "" { - return "", 0 - } - baseURL = strings.TrimPrefix(baseURL, "http://") - baseURL = strings.TrimPrefix(baseURL, "https://") - parts := strings.SplitN(baseURL, ":", 2) - host := parts[0] - if len(parts) == 2 { - httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/")) - if err == nil { - switch httpPort { - case 6333: - return host, 6334 - case 6334: - return host, 6334 - default: - // Common case: operator already configured the intended gRPC port. - return host, httpPort - } - } - } - return host, 6334 -} diff --git a/internal/memory/adapters/builtin/file_runtime_test.go b/internal/memory/adapters/builtin/file_runtime_test.go index a1dca69fe1..28f14a267d 100644 --- a/internal/memory/adapters/builtin/file_runtime_test.go +++ b/internal/memory/adapters/builtin/file_runtime_test.go @@ -12,7 +12,7 @@ import ( func TestFileRuntimeRejectsEmptyMemoryWithoutHTTPError(t *testing.T) { t.Parallel() - runtime := newFileRuntime(newFakeSparseStore()) + runtime := newFileRuntime(newFakeStore()) _, err := runtime.Add(context.Background(), adapters.AddRequest{BotID: "bot-1"}) if err == nil { @@ -28,7 +28,7 @@ func TestFileRuntimeRejectsEmptyMemoryWithoutHTTPError(t *testing.T) { func TestFileRuntimeCompactWithLLMArchivesSourceMemories(t *testing.T) { t.Parallel() - store := newFakeSparseStore( + store := newFakeStore( storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "Ran likes green tea", CreatedAt: "2026-06-01T00:00:00Z", UpdatedAt: "2026-06-01T00:00:00Z"}, storefs.MemoryItem{ID: "bot-1:mem_2", Memory: "Ran likes oolong tea", CreatedAt: "2026-06-02T00:00:00Z", UpdatedAt: "2026-06-02T00:00:00Z"}, ) diff --git a/internal/memory/adapters/builtin/formation_test.go b/internal/memory/adapters/builtin/formation_test.go index e052836812..d2e616e5f0 100644 --- a/internal/memory/adapters/builtin/formation_test.go +++ b/internal/memory/adapters/builtin/formation_test.go @@ -45,10 +45,8 @@ func (f *fakeLLM) Compact(_ context.Context, req adapters.CompactRequest) (adapt func TestFormationExtractAndAdd(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{"User likes oolong tea", "User is based in Berlin"}, decideActions: []adapters.DecisionAction{ @@ -84,10 +82,8 @@ func TestFormationExtractAndAdd(t *testing.T) { func TestFormationUpdate(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) addResp, err := runtime.Add(context.Background(), adapters.AddRequest{ BotID: "bot-1", @@ -131,10 +127,8 @@ func TestFormationUpdate(t *testing.T) { func TestFormationDelete(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) addResp, err := runtime.Add(context.Background(), adapters.AddRequest{ BotID: "bot-1", @@ -170,10 +164,8 @@ func TestFormationDelete(t *testing.T) { func TestFormationNOOP(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{"User likes tea"}, @@ -202,10 +194,8 @@ func TestFormationNOOP(t *testing.T) { func TestFormationNoFacts(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{}, @@ -229,10 +219,8 @@ func TestFormationNoFacts(t *testing.T) { func TestFormationMixedActions(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) addResp, _ := runtime.Add(context.Background(), adapters.AddRequest{ BotID: "bot-1", @@ -273,10 +261,8 @@ func TestFormationMixedActions(t *testing.T) { func TestFormationInvalidActionsSkipped(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{"User likes cats"}, @@ -306,10 +292,8 @@ func TestFormationInvalidActionsSkipped(t *testing.T) { func TestFormationDuplicateActionsSameID(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) addResp, _ := runtime.Add(context.Background(), adapters.AddRequest{ BotID: "bot-1", @@ -343,10 +327,8 @@ func TestFormationDuplicateActionsSameID(t *testing.T) { func TestOnAfterChatWithLLM(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{"User prefers dark mode"}, decideActions: []adapters.DecisionAction{ @@ -379,10 +361,8 @@ func TestOnAfterChatWithLLM(t *testing.T) { func TestOnAfterChatFallbackWithoutLLM(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) p := NewBuiltinProvider(slog.Default(), runtime, nil, nil) @@ -402,10 +382,8 @@ func TestOnAfterChatFallbackWithoutLLM(t *testing.T) { func TestOnBeforeChatRecallsFactMemory(t *testing.T) { t.Parallel() - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{qdrant: index, encoder: encoder, store: store} + store := newFakeStore() + runtime := newFileRuntime(store) llm := &fakeLLM{ extractFacts: []string{"User prefers oolong tea"}, decideActions: []adapters.DecisionAction{ diff --git a/internal/memory/adapters/builtin/shared.go b/internal/memory/adapters/builtin/shared.go index 781c169dd0..4883315728 100644 --- a/internal/memory/adapters/builtin/shared.go +++ b/internal/memory/adapters/builtin/shared.go @@ -2,10 +2,13 @@ package builtin import ( "context" + "crypto/sha256" + "encoding/hex" "errors" "fmt" "strconv" "strings" + "sync/atomic" "time" "github.com/google/uuid" @@ -15,6 +18,13 @@ import ( storefs "github.com/memohai/memoh/internal/memory/storefs" ) +// memoryIDSeq is a process-wide monotonic counter appended to memory IDs so +// that two Add calls landing in the same wall-clock nanosecond still produce +// distinct IDs. The wall-clock nanosecond remains the dominant component, so +// IDs stay human-readable and roughly time-ordered; the sequence only breaks +// ties when the clock has not advanced. +var memoryIDSeq uint64 + // memoryStore is the markdown file store consumed by the builtin runtimes. type memoryStore interface { PersistMemories(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error @@ -181,7 +191,16 @@ func runtimeText(message string, messages []adapters.Message) string { } func runtimeMemoryID(botID string, now time.Time) string { - return botID + ":" + "mem_" + strconv.FormatInt(now.UnixNano(), 10) + seq := atomic.AddUint64(&memoryIDSeq, 1) + return botID + ":" + "mem_" + strconv.FormatInt(now.UnixNano(), 10) + "_" + strconv.FormatUint(seq, 36) +} + +// runtimeHash returns a stable SHA-256 hex digest of a trimmed memory body. +// It is shared by the dense and file runtimes (and the upcoming graph runtime) +// for content-addressing memory items. +func runtimeHash(text string) string { + sum := sha256.Sum256([]byte(strings.TrimSpace(text))) + return hex.EncodeToString(sum[:]) } func runtimePointID(botID, sourceID string) string { diff --git a/internal/memory/adapters/builtin/sparse_runtime.go b/internal/memory/adapters/builtin/sparse_runtime.go deleted file mode 100644 index c262e2b1c0..0000000000 --- a/internal/memory/adapters/builtin/sparse_runtime.go +++ /dev/null @@ -1,618 +0,0 @@ -package builtin - -import ( - "context" - "errors" - "fmt" - "path" - "sort" - "strings" - "time" - - "github.com/memohai/memoh/internal/config" - adapters "github.com/memohai/memoh/internal/memory/adapters" - qdrantclient "github.com/memohai/memoh/internal/memory/qdrant" - "github.com/memohai/memoh/internal/memory/sparse" - storefs "github.com/memohai/memoh/internal/memory/storefs" -) - -type sparseEncoder interface { - EncodeDocument(ctx context.Context, text string) (*sparse.SparseVector, error) - EncodeDocuments(ctx context.Context, texts []string) ([]sparse.SparseVector, error) - EncodeQuery(ctx context.Context, text string) (*sparse.SparseVector, error) - Health(ctx context.Context) error -} - -type sparseIndex interface { - CollectionName() string - CollectionExists(ctx context.Context) (bool, error) - EnsureCollection(ctx context.Context) error - Upsert(ctx context.Context, id string, vec qdrantclient.SparseVector, payload map[string]string) error - Search(ctx context.Context, vec qdrantclient.SparseVector, botID string, limit int) ([]qdrantclient.SearchResult, error) - Scroll(ctx context.Context, botID string, limit int) ([]qdrantclient.SearchResult, error) - Count(ctx context.Context, botID string) (int, error) - DeleteByIDs(ctx context.Context, ids []string) error - DeleteByBotID(ctx context.Context, botID string) error -} - -// sparseRuntime implements Runtime with markdown files as the source of -// truth and Qdrant as a derived sparse index used for retrieval. -type sparseRuntime struct { - qdrant sparseIndex - encoder sparseEncoder - store memoryStore -} - -const ( - sparseExplainTopKLimit = 24 -) - -func newSparseRuntime(qdrantHost string, qdrantPort int, qdrantAPIKey, collection, encoderBaseURL string, store *storefs.Service) (*sparseRuntime, error) { - if strings.TrimSpace(qdrantHost) == "" { - return nil, errors.New("sparse runtime: qdrant host is required") - } - if strings.TrimSpace(encoderBaseURL) == "" { - return nil, errors.New("sparse runtime: sparse.base_url is required") - } - if store == nil { - return nil, errors.New("sparse runtime: memory store is required") - } - qClient, err := qdrantclient.NewClient(qdrantHost, qdrantPort, qdrantAPIKey, collection) - if err != nil { - return nil, fmt.Errorf("sparse runtime: %w", err) - } - return &sparseRuntime{ - qdrant: qClient, - encoder: sparse.NewClient(encoderBaseURL), - store: store, - }, nil -} - -func (r *sparseRuntime) ensureCollection(ctx context.Context) error { - return r.qdrant.EnsureCollection(ctx) -} - -func (*sparseRuntime) Mode() string { - return string(ModeSparse) -} - -func (r *sparseRuntime) Add(ctx context.Context, req adapters.AddRequest) (adapters.SearchResponse, error) { - botID, err := runtimeBotID(req.BotID, req.Filters) - if err != nil { - return adapters.SearchResponse{}, err - } - text := runtimeText(req.Message, req.Messages) - if text == "" { - return adapters.SearchResponse{}, errors.New("sparse runtime: message is required") - } - - now := time.Now().UTC().Format(time.RFC3339) - item := adapters.MemoryItem{ - ID: runtimeMemoryID(botID, time.Now().UTC()), - Memory: text, - Hash: runtimeHash(text), - Metadata: req.Metadata, - BotID: botID, - CreatedAt: now, - UpdatedAt: now, - } - if err := r.store.PersistMemories(ctx, botID, []storefs.MemoryItem{storeItemFromMemoryItem(item)}, req.Filters); err != nil { - return adapters.SearchResponse{}, err - } - if err := r.upsertSourceItems(ctx, botID, []storefs.MemoryItem{storeItemFromMemoryItem(item)}); err != nil { - return adapters.SearchResponse{}, err - } - return adapters.SearchResponse{Results: []adapters.MemoryItem{item}}, nil -} - -func (r *sparseRuntime) Search(ctx context.Context, req adapters.SearchRequest) (adapters.SearchResponse, error) { - botID, err := runtimeBotID(req.BotID, req.Filters) - if err != nil { - return adapters.SearchResponse{}, err - } - if err := r.ensureCollection(ctx); err != nil { - return adapters.SearchResponse{}, err - } - - limit := req.Limit - if limit <= 0 { - limit = 10 - } - - vec, err := r.encoder.EncodeQuery(ctx, req.Query) - if err != nil { - return adapters.SearchResponse{}, fmt.Errorf("sparse encode query: %w", err) - } - results, err := r.qdrant.Search(ctx, qdrantclient.SparseVector{ - Indices: vec.Indices, - Values: vec.Values, - }, botID, limit) - if err != nil { - return adapters.SearchResponse{}, err - } - items := make([]adapters.MemoryItem, 0, len(results)) - for _, r := range results { - items = append(items, resultToItem(r)) - } - return adapters.SearchResponse{Results: items}, nil -} - -func (r *sparseRuntime) GetAll(ctx context.Context, req adapters.GetAllRequest) (adapters.SearchResponse, error) { - botID, err := runtimeBotID(req.BotID, req.Filters) - if err != nil { - return adapters.SearchResponse{}, err - } - items, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.SearchResponse{}, err - } - result := make([]adapters.MemoryItem, 0, len(items)) - for _, item := range items { - mem := memoryItemFromStore(item) - mem.BotID = botID - result = append(result, mem) - } - r.populateExplainStats(ctx, sparseMemoryItemPointers(result)) - sort.Slice(result, func(i, j int) bool { return result[i].UpdatedAt > result[j].UpdatedAt }) - if req.Limit > 0 && len(result) > req.Limit { - result = result[:req.Limit] - } - return adapters.SearchResponse{Results: result}, nil -} - -func (r *sparseRuntime) Update(ctx context.Context, req adapters.UpdateRequest) (adapters.MemoryItem, error) { - memoryID := strings.TrimSpace(req.MemoryID) - if memoryID == "" { - return adapters.MemoryItem{}, errors.New("sparse runtime: memory_id is required") - } - text := strings.TrimSpace(req.Memory) - if text == "" { - return adapters.MemoryItem{}, errors.New("sparse runtime: memory is required") - } - botID := runtimeBotIDFromMemoryID(memoryID) - if botID == "" { - return adapters.MemoryItem{}, errors.New("sparse runtime: invalid memory_id") - } - items, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.MemoryItem{}, err - } - var existing *storefs.MemoryItem - for i := range items { - if strings.TrimSpace(items[i].ID) == memoryID { - item := items[i] - existing = &item - break - } - } - if existing == nil { - return adapters.MemoryItem{}, errors.New("sparse runtime: memory not found") - } - existing.Memory = text - existing.Hash = runtimeHash(text) - existing.UpdatedAt = time.Now().UTC().Format(time.RFC3339) - if err := r.store.PersistMemories(ctx, botID, []storefs.MemoryItem{*existing}, nil); err != nil { - return adapters.MemoryItem{}, err - } - if err := r.upsertSourceItems(ctx, botID, []storefs.MemoryItem{*existing}); err != nil { - return adapters.MemoryItem{}, err - } - item := memoryItemFromStore(*existing) - item.BotID = botID - return item, nil -} - -func (r *sparseRuntime) Delete(ctx context.Context, memoryID string) (adapters.DeleteResponse, error) { - return r.DeleteBatch(ctx, []string{memoryID}) -} - -func (r *sparseRuntime) DeleteBatch(ctx context.Context, memoryIDs []string) (adapters.DeleteResponse, error) { - grouped := map[string][]string{} - pointIDs := make([]string, 0, len(memoryIDs)) - for _, rawID := range memoryIDs { - memoryID := strings.TrimSpace(rawID) - if memoryID == "" { - continue - } - botID := runtimeBotIDFromMemoryID(memoryID) - if botID == "" { - continue - } - grouped[botID] = append(grouped[botID], memoryID) - pointIDs = append(pointIDs, runtimePointID(botID, memoryID)) - } - for botID, ids := range grouped { - if err := r.store.RemoveMemories(ctx, botID, ids); err != nil { - return adapters.DeleteResponse{}, err - } - } - if err := r.ensureCollection(ctx); err != nil { - return adapters.DeleteResponse{}, err - } - if err := r.qdrant.DeleteByIDs(ctx, pointIDs); err != nil { - return adapters.DeleteResponse{}, err - } - return adapters.DeleteResponse{Message: "Memories deleted successfully!"}, nil -} - -func (r *sparseRuntime) DeleteAll(ctx context.Context, req adapters.DeleteAllRequest) (adapters.DeleteResponse, error) { - botID, err := runtimeBotID(req.BotID, req.Filters) - if err != nil { - return adapters.DeleteResponse{}, err - } - if err := r.store.RemoveAllMemories(ctx, botID); err != nil { - return adapters.DeleteResponse{}, err - } - if err := r.ensureCollection(ctx); err != nil { - return adapters.DeleteResponse{}, err - } - if err := r.qdrant.DeleteByBotID(ctx, botID); err != nil { - return adapters.DeleteResponse{}, err - } - return adapters.DeleteResponse{Message: "All memories deleted successfully!"}, nil -} - -func (r *sparseRuntime) Compact(ctx context.Context, filters map[string]any, ratio float64, _ int) (adapters.CompactResult, error) { - botID, err := runtimeBotID("", filters) - if err != nil { - return adapters.CompactResult{}, err - } - all, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.CompactResult{}, err - } - before := len(all) - if before == 0 { - return adapters.CompactResult{Ratio: ratio}, nil - } - - sort.Slice(all, func(i, j int) bool { - return all[i].UpdatedAt > all[j].UpdatedAt - }) - target := int(float64(before) * ratio) - if target < 1 { - target = 1 - } - if target > before { - target = before - } - keptStore := append([]storefs.MemoryItem(nil), all[:target]...) - if err := r.store.RebuildFiles(ctx, botID, keptStore, filters); err != nil { - return adapters.CompactResult{}, err - } - if _, err := r.Rebuild(ctx, botID); err != nil { - return adapters.CompactResult{}, err - } - kept := make([]adapters.MemoryItem, 0, len(keptStore)) - for _, item := range keptStore { - kept = append(kept, memoryItemFromStore(item)) - } - return adapters.CompactResult{ - BeforeCount: before, - AfterCount: len(kept), - Ratio: ratio, - Results: kept, - }, nil -} - -func (r *sparseRuntime) CompactWithLLM(ctx context.Context, filters map[string]any, ratio float64, decayDays int, llm adapters.LLM) (adapters.CompactResult, error) { - botID, err := runtimeBotID("", filters) - if err != nil { - return adapters.CompactResult{}, err - } - if ratio <= 0 || ratio > 1 { - return adapters.CompactResult{}, errors.New("ratio must be in range (0, 1]") - } - all, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.CompactResult{}, err - } - before := len(all) - if before == 0 { - return adapters.CompactResult{BeforeCount: 0, AfterCount: 0, Ratio: ratio, Results: []adapters.MemoryItem{}}, nil - } - sort.Slice(all, func(i, j int) bool { - return all[i].UpdatedAt > all[j].UpdatedAt - }) - compactedStore, archivedStore, err := compactStoreItemsWithLLM(ctx, botID, all, ratio, decayDays, llm) - if err != nil { - return adapters.CompactResult{}, err - } - if err := r.store.ArchiveAndRebuildFiles(ctx, botID, compactedStore, archivedStore, filters); err != nil { - return adapters.CompactResult{}, err - } - if _, err := r.Rebuild(ctx, botID); err != nil { - return adapters.CompactResult{}, err - } - compacted := make([]adapters.MemoryItem, 0, len(compactedStore)) - for _, item := range compactedStore { - compacted = append(compacted, memoryItemFromStore(item)) - } - return adapters.CompactResult{ - BeforeCount: before, - AfterCount: len(compacted), - Ratio: ratio, - Results: compacted, - }, nil -} - -func (r *sparseRuntime) Usage(ctx context.Context, filters map[string]any) (adapters.UsageResponse, error) { - botID, err := runtimeBotID("", filters) - if err != nil { - return adapters.UsageResponse{}, err - } - items, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.UsageResponse{}, err - } - var usage adapters.UsageResponse - usage.Count = len(items) - for _, item := range items { - usage.TotalTextBytes += int64(len(item.Memory)) - } - if usage.Count > 0 { - usage.AvgTextBytes = usage.TotalTextBytes / int64(usage.Count) - } - usage.EstimatedStorageBytes = usage.TotalTextBytes - return usage, nil -} - -func (r *sparseRuntime) Status(ctx context.Context, botID string) (adapters.MemoryStatusResponse, error) { - fileCount, err := r.store.CountMemoryFiles(ctx, botID) - if err != nil { - return adapters.MemoryStatusResponse{}, err - } - items, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.MemoryStatusResponse{}, err - } - status := adapters.MemoryStatusResponse{ - ProviderType: BuiltinType, - MemoryMode: string(ModeSparse), - CanManualSync: true, - SourceDir: path.Join(config.DefaultDataMount, "memory"), - OverviewPath: path.Join(config.DefaultDataMount, "MEMORY.md"), - MarkdownFileCount: fileCount, - SourceCount: len(items), - QdrantCollection: r.qdrant.CollectionName(), - } - if err := r.encoder.Health(ctx); err != nil { - status.Encoder.Error = err.Error() - } else { - status.Encoder.OK = true - } - exists, err := r.qdrant.CollectionExists(ctx) - if err != nil { - status.Qdrant.Error = err.Error() - return status, nil - } - status.Qdrant.OK = true - if exists { - count, err := r.qdrant.Count(ctx, botID) - if err != nil { - status.Qdrant.OK = false - status.Qdrant.Error = err.Error() - return status, nil - } - status.IndexedCount = count - } - return status, nil -} - -func (r *sparseRuntime) Rebuild(ctx context.Context, botID string) (adapters.RebuildResult, error) { - items, err := r.store.ReadAllMemoryFiles(ctx, botID) - if err != nil { - return adapters.RebuildResult{}, err - } - if err := r.store.SyncOverview(ctx, botID); err != nil { - return adapters.RebuildResult{}, err - } - return r.syncSourceItems(ctx, botID, items) -} - -// --- helpers --- - -func (r *sparseRuntime) syncSourceItems(ctx context.Context, botID string, items []storefs.MemoryItem) (adapters.RebuildResult, error) { - if err := r.ensureCollection(ctx); err != nil { - return adapters.RebuildResult{}, err - } - existing, err := r.qdrant.Scroll(ctx, botID, 10000) - if err != nil { - return adapters.RebuildResult{}, err - } - existingBySource := make(map[string]qdrantclient.SearchResult, len(existing)) - for _, item := range existing { - sourceID := strings.TrimSpace(item.Payload["source_entry_id"]) - if sourceID == "" { - sourceID = strings.TrimSpace(item.ID) - } - if sourceID == "" { - continue - } - existingBySource[sourceID] = item - } - canonical := make([]storefs.MemoryItem, 0, len(items)) - sourceIDs := make(map[string]struct{}, len(items)) - toUpsert := make([]storefs.MemoryItem, 0, len(items)) - missingCount := 0 - restoredCount := 0 - for _, item := range items { - item = canonicalStoreItem(item) - if item.ID == "" || item.Memory == "" { - continue - } - canonical = append(canonical, item) - sourceIDs[item.ID] = struct{}{} - payload := runtimePayload(botID, item) - existingItem, ok := existingBySource[item.ID] - if !ok { - missingCount++ - restoredCount++ - toUpsert = append(toUpsert, item) - continue - } - if !payloadMatches(existingItem.Payload, payload) { - restoredCount++ - toUpsert = append(toUpsert, item) - } - } - stalePointIDs := make([]string, 0) - for _, item := range existing { - sourceID := strings.TrimSpace(item.Payload["source_entry_id"]) - if sourceID == "" { - sourceID = strings.TrimSpace(item.ID) - } - if _, ok := sourceIDs[sourceID]; ok { - continue - } - if strings.TrimSpace(item.ID) != "" { - stalePointIDs = append(stalePointIDs, item.ID) - } - } - if len(stalePointIDs) > 0 { - if err := r.qdrant.DeleteByIDs(ctx, stalePointIDs); err != nil { - return adapters.RebuildResult{}, err - } - } - if err := r.upsertSourceItems(ctx, botID, toUpsert); err != nil { - return adapters.RebuildResult{}, err - } - count, err := r.qdrant.Count(ctx, botID) - if err != nil { - return adapters.RebuildResult{}, err - } - return adapters.RebuildResult{ - FsCount: len(canonical), - StorageCount: count, - MissingCount: missingCount, - RestoredCount: restoredCount, - }, nil -} - -func (r *sparseRuntime) upsertSourceItems(ctx context.Context, botID string, items []storefs.MemoryItem) error { - if len(items) == 0 { - return nil - } - if err := r.ensureCollection(ctx); err != nil { - return err - } - texts := make([]string, 0, len(items)) - canonical := make([]storefs.MemoryItem, 0, len(items)) - for _, item := range items { - item = canonicalStoreItem(item) - if item.ID == "" || item.Memory == "" { - continue - } - canonical = append(canonical, item) - texts = append(texts, item.Memory) - } - if len(canonical) == 0 { - return nil - } - vectors, err := r.encoder.EncodeDocuments(ctx, texts) - if err != nil { - return fmt.Errorf("sparse encode documents: %w", err) - } - if len(vectors) != len(canonical) { - return fmt.Errorf("sparse encode documents: expected %d vectors, got %d", len(canonical), len(vectors)) - } - for i, item := range canonical { - vec := vectors[i] - if err := r.qdrant.Upsert(ctx, runtimePointID(botID, item.ID), qdrantclient.SparseVector{ - Indices: vec.Indices, - Values: vec.Values, - }, runtimePayload(botID, item)); err != nil { - return err - } - } - return nil -} - -func (r *sparseRuntime) populateExplainStats(ctx context.Context, items []*adapters.MemoryItem) { - if len(items) == 0 { - return - } - texts := make([]string, 0, len(items)) - targets := make([]*adapters.MemoryItem, 0, len(items)) - for _, item := range items { - if item == nil || strings.TrimSpace(item.Memory) == "" { - continue - } - texts = append(texts, item.Memory) - targets = append(targets, item) - } - if len(texts) == 0 { - return - } - vectors, err := r.encoder.EncodeDocuments(ctx, texts) - if err != nil || len(vectors) != len(targets) { - return - } - for i := range targets { - topK, cdf := sparseExplainStats(vectors[i]) - targets[i].TopKBuckets = topK - targets[i].CDFCurve = cdf - } -} - -func sparseExplainStats(vec sparse.SparseVector) ([]adapters.TopKBucket, []adapters.CDFPoint) { - type pair struct { - index uint32 - value float32 - } - pairs := make([]pair, 0, len(vec.Values)) - for i, value := range vec.Values { - if i >= len(vec.Indices) || value <= 0 { - continue - } - pairs = append(pairs, pair{index: vec.Indices[i], value: value}) - } - if len(pairs) == 0 { - return nil, nil - } - sort.Slice(pairs, func(i, j int) bool { - if pairs[i].value == pairs[j].value { - return pairs[i].index < pairs[j].index - } - return pairs[i].value > pairs[j].value - }) - topN := len(pairs) - if topN > sparseExplainTopKLimit { - topN = sparseExplainTopKLimit - } - topK := make([]adapters.TopKBucket, 0, topN) - total := 0.0 - for _, pair := range pairs { - total += float64(pair.value) - } - for _, pair := range pairs[:topN] { - topK = append(topK, adapters.TopKBucket{ - Index: pair.index, - Value: pair.value, - }) - } - cdf := make([]adapters.CDFPoint, 0, len(pairs)) - if total <= 0 { - return topK, cdf - } - running := 0.0 - for i, pair := range pairs { - running += float64(pair.value) - cdf = append(cdf, adapters.CDFPoint{ - K: i + 1, - Cumulative: running / total, - }) - } - return topK, cdf -} - -func sparseMemoryItemPointers(items []adapters.MemoryItem) []*adapters.MemoryItem { - if len(items) == 0 { - return nil - } - pointers := make([]*adapters.MemoryItem, 0, len(items)) - for i := range items { - pointers = append(pointers, &items[i]) - } - return pointers -} diff --git a/internal/memory/adapters/builtin/sparse_runtime_test.go b/internal/memory/adapters/builtin/sparse_runtime_test.go deleted file mode 100644 index 03465d406c..0000000000 --- a/internal/memory/adapters/builtin/sparse_runtime_test.go +++ /dev/null @@ -1,419 +0,0 @@ -package builtin - -import ( - "context" - "log/slog" - "sort" - "strings" - "testing" - - adapters "github.com/memohai/memoh/internal/memory/adapters" - qdrantclient "github.com/memohai/memoh/internal/memory/qdrant" - "github.com/memohai/memoh/internal/memory/sparse" - storefs "github.com/memohai/memoh/internal/memory/storefs" -) - -type fakeSparseStore struct { - items map[string]storefs.MemoryItem - archive []storefs.MemoryItem -} - -func newFakeSparseStore(items ...storefs.MemoryItem) *fakeSparseStore { - store := &fakeSparseStore{items: map[string]storefs.MemoryItem{}} - for _, item := range items { - store.items[item.ID] = item - } - return store -} - -func (s *fakeSparseStore) PersistMemories(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error { - for _, item := range items { - s.items[item.ID] = item - } - return nil -} - -func (s *fakeSparseStore) ReadAllMemoryFiles(_ context.Context, _ string) ([]storefs.MemoryItem, error) { - out := make([]storefs.MemoryItem, 0, len(s.items)) - for _, item := range s.items { - out = append(out, item) - } - sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) - return out, nil -} - -func (s *fakeSparseStore) RemoveMemories(_ context.Context, _ string, ids []string) error { - for _, id := range ids { - delete(s.items, strings.TrimSpace(id)) - } - return nil -} - -func (s *fakeSparseStore) RemoveAllMemories(_ context.Context, _ string) error { - s.items = map[string]storefs.MemoryItem{} - return nil -} - -func (s *fakeSparseStore) RebuildFiles(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error { - s.items = map[string]storefs.MemoryItem{} - for _, item := range items { - s.items[item.ID] = item - } - return nil -} - -func (s *fakeSparseStore) ArchiveAndRebuildFiles(ctx context.Context, _ string, active []storefs.MemoryItem, archived []storefs.MemoryItem, _ map[string]any) error { - s.archive = append([]storefs.MemoryItem(nil), archived...) - return s.RebuildFiles(ctx, "", active, nil) -} - -func (*fakeSparseStore) SyncOverview(context.Context, string) error { return nil } - -func (s *fakeSparseStore) CountMemoryFiles(_ context.Context, _ string) (int, error) { - if len(s.items) == 0 { - return 0, nil - } - return 1, nil -} - -type fakeSparseEncoder struct { - lastQuery string -} - -func (*fakeSparseEncoder) EncodeDocument(_ context.Context, _ string) (*sparse.SparseVector, error) { - return &sparse.SparseVector{Indices: []uint32{1, 2, 3}, Values: []float32{1, 3, 2}}, nil -} - -func (*fakeSparseEncoder) EncodeDocuments(_ context.Context, texts []string) ([]sparse.SparseVector, error) { - out := make([]sparse.SparseVector, 0, len(texts)) - for _, text := range texts { - _ = text - out = append(out, sparse.SparseVector{Indices: []uint32{1, 2, 3}, Values: []float32{1, 3, 2}}) - } - return out, nil -} - -func (e *fakeSparseEncoder) EncodeQuery(_ context.Context, text string) (*sparse.SparseVector, error) { - e.lastQuery = text - return &sparse.SparseVector{Indices: []uint32{9}, Values: []float32{1}}, nil -} - -func (*fakeSparseEncoder) Health(context.Context) error { return nil } - -type fakeSparseIndex struct { - encoder *fakeSparseEncoder - collection string - exists bool - points map[string]qdrantclient.SearchResult -} - -func newFakeSparseIndex(encoder *fakeSparseEncoder) *fakeSparseIndex { - return &fakeSparseIndex{ - encoder: encoder, - collection: "memory_sparse_test", - points: map[string]qdrantclient.SearchResult{}, - } -} - -func (i *fakeSparseIndex) CollectionName() string { return i.collection } - -func (i *fakeSparseIndex) CollectionExists(context.Context) (bool, error) { return i.exists, nil } - -func (i *fakeSparseIndex) EnsureCollection(context.Context) error { - i.exists = true - return nil -} - -func (i *fakeSparseIndex) Upsert(_ context.Context, id string, _ qdrantclient.SparseVector, payload map[string]string) error { - i.exists = true - i.points[id] = qdrantclient.SearchResult{ - ID: id, - Score: 1, - Payload: payload, - } - return nil -} - -func (i *fakeSparseIndex) Search(_ context.Context, _ qdrantclient.SparseVector, botID string, limit int) ([]qdrantclient.SearchResult, error) { - query := strings.ToLower(strings.TrimSpace(i.encoder.lastQuery)) - results := make([]qdrantclient.SearchResult, 0, len(i.points)) - for _, point := range i.points { - if strings.TrimSpace(point.Payload["bot_id"]) != strings.TrimSpace(botID) { - continue - } - text := strings.ToLower(point.Payload["memory"]) - if query != "" && !strings.Contains(text, query) { - continue - } - point.Score = 1 - results = append(results, point) - } - sort.Slice(results, func(a, b int) bool { return results[a].ID < results[b].ID }) - if limit > 0 && len(results) > limit { - results = results[:limit] - } - return results, nil -} - -func (i *fakeSparseIndex) Scroll(_ context.Context, botID string, limit int) ([]qdrantclient.SearchResult, error) { - results := make([]qdrantclient.SearchResult, 0, len(i.points)) - for _, point := range i.points { - if strings.TrimSpace(point.Payload["bot_id"]) != strings.TrimSpace(botID) { - continue - } - results = append(results, point) - } - sort.Slice(results, func(a, b int) bool { return results[a].ID < results[b].ID }) - if limit > 0 && len(results) > limit { - results = results[:limit] - } - return results, nil -} - -func (i *fakeSparseIndex) Count(_ context.Context, botID string) (int, error) { - count := 0 - for _, point := range i.points { - if strings.TrimSpace(point.Payload["bot_id"]) == strings.TrimSpace(botID) { - count++ - } - } - return count, nil -} - -func (i *fakeSparseIndex) DeleteByIDs(_ context.Context, ids []string) error { - for _, id := range ids { - delete(i.points, strings.TrimSpace(id)) - } - return nil -} - -func (i *fakeSparseIndex) DeleteByBotID(_ context.Context, botID string) error { - for id, point := range i.points { - if strings.TrimSpace(point.Payload["bot_id"]) == strings.TrimSpace(botID) { - delete(i.points, id) - } - } - return nil -} - -func TestSparseRuntimeAddWritesSourceAndSupportsRecall(t *testing.T) { - t.Parallel() - - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{ - qdrant: index, - encoder: encoder, - store: store, - } - - resp, err := runtime.Add(context.Background(), adapters.AddRequest{ - BotID: "bot-1", - Message: "Ran likes oolong tea", - Filters: map[string]any{"scopeId": "bot-1"}, - }) - if err != nil { - t.Fatalf("Add() error = %v", err) - } - if len(resp.Results) != 1 { - t.Fatalf("expected 1 add result, got %d", len(resp.Results)) - } - item := resp.Results[0] - if item.ID == "" { - t.Fatal("expected source memory id to be populated") - } - if _, ok := store.items[item.ID]; !ok { - t.Fatalf("expected memory %q to be written to markdown source", item.ID) - } - point, ok := index.points[runtimePointID("bot-1", item.ID)] - if !ok { - t.Fatalf("expected qdrant point for source memory %q", item.ID) - } - if point.Payload["source_entry_id"] != item.ID { - t.Fatalf("expected source_entry_id payload %q, got %q", item.ID, point.Payload["source_entry_id"]) - } - if len(item.TopKBuckets) != 0 || len(item.CDFCurve) != 0 { - t.Fatalf("expected add response to skip explain stats, got %#v", item) - } - - searchResp, err := runtime.Search(context.Background(), adapters.SearchRequest{ - BotID: "bot-1", - Query: "oolong tea", - }) - if err != nil { - t.Fatalf("Search() error = %v", err) - } - if len(searchResp.Results) != 1 { - t.Fatalf("expected 1 search result, got %d", len(searchResp.Results)) - } - if searchResp.Results[0].ID != item.ID { - t.Fatalf("expected search result id %q, got %q", item.ID, searchResp.Results[0].ID) - } - if len(searchResp.Results[0].TopKBuckets) != 0 || len(searchResp.Results[0].CDFCurve) != 0 { - t.Fatalf("expected search result to skip explain stats, got %#v", searchResp.Results[0]) - } -} - -func TestSparseRuntimeRebuildSyncsSourceAndRemovesStalePoints(t *testing.T) { - t.Parallel() - - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore( - storefs.MemoryItem{ - ID: "bot-1:mem_1", - Memory: "Ran likes tea", - Hash: runtimeHash("Ran likes tea"), - CreatedAt: "2026-03-13T09:00:00Z", - UpdatedAt: "2026-03-13T09:00:00Z", - }, - storefs.MemoryItem{ - ID: "bot-1:mem_2", - Memory: "Ran works in Berlin", - Hash: runtimeHash("Ran works in Berlin"), - CreatedAt: "2026-03-13T10:00:00Z", - UpdatedAt: "2026-03-13T10:00:00Z", - }, - ) - runtime := &sparseRuntime{ - qdrant: index, - encoder: encoder, - store: store, - } - - index.points[runtimePointID("bot-1", "bot-1:mem_1")] = qdrantclient.SearchResult{ - ID: runtimePointID("bot-1", "bot-1:mem_1"), - Payload: map[string]string{ - "bot_id": "bot-1", - "memory": "Ran likes tea", - "source_entry_id": "bot-1:mem_1", - "hash": "outdated", - "created_at": "2026-03-13T09:00:00Z", - "updated_at": "2026-03-13T09:00:00Z", - }, - } - index.points[runtimePointID("bot-1", "bot-1:stale")] = qdrantclient.SearchResult{ - ID: runtimePointID("bot-1", "bot-1:stale"), - Payload: map[string]string{ - "bot_id": "bot-1", - "memory": "stale memory", - "source_entry_id": "bot-1:stale", - }, - } - - result, err := runtime.Rebuild(context.Background(), "bot-1") - if err != nil { - t.Fatalf("Rebuild() error = %v", err) - } - if result.FsCount != 2 { - t.Fatalf("expected fs_count=2, got %d", result.FsCount) - } - if result.StorageCount != 2 { - t.Fatalf("expected storage_count=2, got %d", result.StorageCount) - } - if result.MissingCount != 1 { - t.Fatalf("expected missing_count=1, got %d", result.MissingCount) - } - if result.RestoredCount != 2 { - t.Fatalf("expected restored_count=2, got %d", result.RestoredCount) - } - if _, ok := index.points[runtimePointID("bot-1", "bot-1:stale")]; ok { - t.Fatal("expected stale qdrant point to be removed") - } -} - -func TestSparseRuntimeGetAllIncludesExplainStats(t *testing.T) { - t.Parallel() - - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore( - storefs.MemoryItem{ - ID: "bot-1:mem_1", - Memory: "Ran likes tea", - Hash: runtimeHash("Ran likes tea"), - CreatedAt: "2026-03-13T09:00:00Z", - UpdatedAt: "2026-03-13T09:00:00Z", - }, - ) - runtime := &sparseRuntime{ - qdrant: index, - encoder: encoder, - store: store, - } - - resp, err := runtime.GetAll(context.Background(), adapters.GetAllRequest{BotID: "bot-1"}) - if err != nil { - t.Fatalf("GetAll() error = %v", err) - } - if len(resp.Results) != 1 { - t.Fatalf("expected 1 result, got %d", len(resp.Results)) - } - if len(resp.Results[0].TopKBuckets) == 0 || len(resp.Results[0].CDFCurve) == 0 { - t.Fatalf("expected get all result to include explain stats, got %#v", resp.Results[0]) - } - if resp.Results[0].TopKBuckets[0].Index != 2 { - t.Fatalf("expected top bucket index 2, got %d", resp.Results[0].TopKBuckets[0].Index) - } - if got := resp.Results[0].CDFCurve[len(resp.Results[0].CDFCurve)-1].Cumulative; got != 1 { - t.Fatalf("expected final CDF cumulative to be 1, got %v", got) - } -} - -func TestBuiltinProviderMultiTurnRecallUsesSparseSourceRuntime(t *testing.T) { - t.Parallel() - - encoder := &fakeSparseEncoder{} - index := newFakeSparseIndex(encoder) - store := newFakeSparseStore() - runtime := &sparseRuntime{ - qdrant: index, - encoder: encoder, - store: store, - } - provider := NewBuiltinProvider(slog.Default(), runtime, nil, nil) - - err := provider.OnAfterChat(context.Background(), adapters.AfterChatRequest{ - BotID: "bot-1", - Messages: []adapters.Message{ - {Role: "user", Content: "I like oolong tea."}, - {Role: "assistant", Content: "Noted, you like oolong tea."}, - }, - }) - if err != nil { - t.Fatalf("OnAfterChat round 1 error = %v", err) - } - err = provider.OnAfterChat(context.Background(), adapters.AfterChatRequest{ - BotID: "bot-1", - Messages: []adapters.Message{ - {Role: "user", Content: "I am based in Berlin."}, - {Role: "assistant", Content: "Understood, you are based in Berlin."}, - }, - }) - if err != nil { - t.Fatalf("OnAfterChat round 2 error = %v", err) - } - - before, err := provider.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{ - BotID: "bot-1", - Query: "berlin", - }) - if err != nil { - t.Fatalf("OnBeforeChat() error = %v", err) - } - if before == nil || !strings.Contains(strings.ToLower(before.ContextText), "berlin") { - t.Fatalf("expected recalled context to mention berlin, got %#v", before) - } - - before, err = provider.OnBeforeChat(context.Background(), adapters.BeforeChatRequest{ - BotID: "bot-1", - Query: "oolong tea", - }) - if err != nil { - t.Fatalf("OnBeforeChat() tea error = %v", err) - } - if before == nil || !strings.Contains(strings.ToLower(before.ContextText), "oolong tea") { - t.Fatalf("expected recalled context to mention oolong tea, got %#v", before) - } -} diff --git a/internal/memory/adapters/builtin/store_test.go b/internal/memory/adapters/builtin/store_test.go new file mode 100644 index 0000000000..9c67685277 --- /dev/null +++ b/internal/memory/adapters/builtin/store_test.go @@ -0,0 +1,77 @@ +package builtin + +import ( + "context" + "sort" + "strings" + + storefs "github.com/memohai/memoh/internal/memory/storefs" +) + +// fakeStore is an in-memory implementation of the memoryStore interface used by +// the file runtime tests in this package. +type fakeStore struct { + items map[string]storefs.MemoryItem + archive []storefs.MemoryItem +} + +func newFakeStore(items ...storefs.MemoryItem) *fakeStore { + store := &fakeStore{items: map[string]storefs.MemoryItem{}} + for _, item := range items { + store.items[item.ID] = item + } + return store +} + +func (s *fakeStore) PersistMemories(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error { + for _, item := range items { + s.items[item.ID] = item + } + return nil +} + +func (s *fakeStore) ReadAllMemoryFiles(_ context.Context, _ string) ([]storefs.MemoryItem, error) { + out := make([]storefs.MemoryItem, 0, len(s.items)) + for _, item := range s.items { + out = append(out, item) + } + sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID }) + return out, nil +} + +func (s *fakeStore) RemoveMemories(_ context.Context, _ string, ids []string) error { + for _, id := range ids { + delete(s.items, strings.TrimSpace(id)) + } + return nil +} + +func (s *fakeStore) RemoveAllMemories(_ context.Context, _ string) error { + s.items = map[string]storefs.MemoryItem{} + return nil +} + +func (s *fakeStore) RebuildFiles(_ context.Context, _ string, items []storefs.MemoryItem, _ map[string]any) error { + s.items = map[string]storefs.MemoryItem{} + for _, item := range items { + s.items[item.ID] = item + } + return nil +} + +func (s *fakeStore) ArchiveAndRebuildFiles(ctx context.Context, _ string, active []storefs.MemoryItem, archived []storefs.MemoryItem, _ map[string]any) error { + s.archive = append([]storefs.MemoryItem(nil), archived...) + return s.RebuildFiles(ctx, "", active, nil) +} + +func (*fakeStore) SyncOverview(context.Context, string) error { return nil } + +func (s *fakeStore) CountMemoryFiles(_ context.Context, _ string) (int, error) { + if len(s.items) == 0 { + return 0, nil + } + return 1, nil +} + +// Compile-time assertion that fakeStore satisfies the memoryStore interface. +var _ memoryStore = (*fakeStore)(nil) diff --git a/internal/memory/adapters/service.go b/internal/memory/adapters/service.go index a593a6db05..bb78e7085a 100644 --- a/internal/memory/adapters/service.go +++ b/internal/memory/adapters/service.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log/slog" - "strconv" "strings" "github.com/memohai/memoh/internal/config" @@ -46,7 +45,7 @@ func (*Service) ListMeta(_ context.Context) []ProviderMeta { "memory_mode": { Type: "select", Title: "Memory Mode", - Description: "off = file-based, sparse = Qdrant sparse vectors, dense = embedding API + Qdrant dense vectors", + Description: "off = file-based, dense = embedding API + Qdrant dense vectors", Required: false, }, "embedding_model_id": { @@ -55,13 +54,6 @@ func (*Service) ListMeta(_ context.Context) []ProviderMeta { Description: "Embedding model for dense vector search (dense mode only)", Required: false, }, - "qdrant_collection": { - Type: "string", - Title: "Qdrant Collection", - Description: "Qdrant collection name for sparse mode. Defaults to memory_sparse.", - Required: false, - Example: "memory_sparse", - }, "context_target_items": { Type: "integer", Title: "Context Target Items", @@ -182,11 +174,11 @@ func (s *Service) Status(ctx context.Context, id string) (ProviderStatusResponse } status.MemoryMode = StringFromConfig(resp.Config, "memory_mode") status.EmbeddingModelID = StringFromConfig(resp.Config, "embedding_model_id") - collections := []string{"memory_sparse", "memory_dense"} + collections := []string{"memory_dense"} status.Collections = make([]ProviderCollectionStatus, 0, len(collections)) for _, collection := range collections { collStatus := ProviderCollectionStatus{Name: collection} - host, port := parseQdrantHostPort(s.cfg.Qdrant.BaseURL) + host, port := qdrantclient.ParseHostPort(s.cfg.Qdrant.BaseURL) client, err := qdrantclient.NewClient(host, port, s.cfg.Qdrant.APIKey, collection) if err != nil { collStatus.Qdrant.Error = err.Error() @@ -359,26 +351,3 @@ func isValidProviderType(t ProviderType) bool { return false } } - -func parseQdrantHostPort(baseURL string) (string, int) { - baseURL = strings.TrimSpace(baseURL) - if baseURL == "" { - return "", 0 - } - baseURL = strings.TrimPrefix(baseURL, "http://") - baseURL = strings.TrimPrefix(baseURL, "https://") - parts := strings.SplitN(baseURL, ":", 2) - host := parts[0] - if len(parts) == 2 { - httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/")) - if err == nil { - switch httpPort { - case 6333, 6334: - return host, 6334 - default: - return host, httpPort - } - } - } - return host, 6334 -} diff --git a/internal/memory/adapters/types.go b/internal/memory/adapters/types.go index c56c2beb95..ab9fe2d6b1 100644 --- a/internal/memory/adapters/types.go +++ b/internal/memory/adapters/types.go @@ -85,57 +85,17 @@ type DeleteAllRequest struct { Filters map[string]any `json:"filters,omitempty"` } -type EmbedInput struct { - Text string `json:"text,omitempty"` - ImageURL string `json:"image_url,omitempty"` - VideoURL string `json:"video_url,omitempty"` -} - -type EmbedUpsertRequest struct { - Type string `json:"type"` - Provider string `json:"provider,omitempty"` - Model string `json:"model,omitempty"` - Input EmbedInput `json:"input"` - Source string `json:"source,omitempty"` - BotID string `json:"bot_id,omitempty"` - AgentID string `json:"agent_id,omitempty"` - RunID string `json:"run_id,omitempty"` - Metadata map[string]any `json:"metadata,omitempty"` - Filters map[string]any `json:"filters,omitempty"` -} - -type EmbedUpsertResponse struct { - Item MemoryItem `json:"item"` - Provider string `json:"provider"` - Model string `json:"model"` - Dimensions int `json:"dimensions"` -} - type MemoryItem struct { - ID string `json:"id"` - Memory string `json:"memory"` - Hash string `json:"hash,omitempty"` - CreatedAt string `json:"created_at,omitempty"` - UpdatedAt string `json:"updated_at,omitempty"` - Score float64 `json:"score,omitempty"` - Metadata map[string]any `json:"metadata,omitempty"` - BotID string `json:"bot_id,omitempty"` - AgentID string `json:"agent_id,omitempty"` - RunID string `json:"run_id,omitempty"` - TopKBuckets []TopKBucket `json:"top_k_buckets,omitempty"` - CDFCurve []CDFPoint `json:"cdf_curve,omitempty"` -} - -// TopKBucket represents one bar in the Top-K sparse dimension bar chart. -type TopKBucket struct { - Index uint32 `json:"index"` // sparse dimension index (term hash) - Value float32 `json:"value"` // weight (term frequency) -} - -// CDFPoint represents one point on the cumulative contribution curve. -type CDFPoint struct { - K int `json:"k"` // rank position (1-based, sorted by value desc) - Cumulative float64 `json:"cumulative"` // cumulative weight fraction [0.0, 1.0] + ID string `json:"id"` + Memory string `json:"memory"` + Hash string `json:"hash,omitempty"` + CreatedAt string `json:"created_at,omitempty"` + UpdatedAt string `json:"updated_at,omitempty"` + Score float64 `json:"score,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` + BotID string `json:"bot_id,omitempty"` + AgentID string `json:"agent_id,omitempty"` + RunID string `json:"run_id,omitempty"` } type SearchResponse struct { @@ -205,7 +165,6 @@ type CompactResult struct { type MemoryCompactCapability struct { Semantic bool `json:"semantic"` - Native bool `json:"native,omitempty"` Archive bool `json:"archive,omitempty"` RebuildIndex bool `json:"rebuild_index,omitempty"` Reason string `json:"reason,omitempty"` diff --git a/internal/memory/qdrant/client.go b/internal/memory/qdrant/client.go index ba52e5cb32..f726f5120d 100644 --- a/internal/memory/qdrant/client.go +++ b/internal/memory/qdrant/client.go @@ -1,5 +1,5 @@ // Package qdrant wraps the official github.com/qdrant/go-client SDK, -// providing a thin facade for sparse-vector memory operations. +// providing a thin facade for dense-vector memory operations. package qdrant import ( @@ -12,11 +12,7 @@ import ( pb "github.com/qdrant/go-client/qdrant" ) -const ( - sparseVectorName = "sparse" -) - -// Client wraps the official Qdrant gRPC client with sparse-memory-specific helpers. +// Client wraps the official Qdrant gRPC client with dense-memory-specific helpers. type Client struct { inner *pb.Client collection string @@ -55,10 +51,6 @@ func (c *Client) Close() error { return c.inner.Close() } -func (c *Client) CollectionName() string { - return c.collection -} - func (c *Client) CollectionExists(ctx context.Context) (bool, error) { exists, err := c.inner.CollectionExists(ctx, c.collection) if err != nil { @@ -67,27 +59,6 @@ func (c *Client) CollectionExists(ctx context.Context) (bool, error) { return exists, nil } -// EnsureCollection creates the collection with a named sparse vector config if it does not exist. -func (c *Client) EnsureCollection(ctx context.Context) error { - exists, err := c.CollectionExists(ctx) - if err != nil { - return err - } - if exists { - return nil - } - err = c.inner.CreateCollection(ctx, &pb.CreateCollection{ - CollectionName: c.collection, - SparseVectorsConfig: pb.NewSparseVectorsConfig(map[string]*pb.SparseVectorParams{ - sparseVectorName: {}, - }), - }) - if err != nil { - return fmt.Errorf("qdrant: create collection: %w", err) - } - return nil -} - // EnsureDenseCollection creates the collection with dense vector config if it // does not exist. func (c *Client) EnsureDenseCollection(ctx context.Context, dimensions int) error { @@ -114,48 +85,17 @@ func (c *Client) EnsureDenseCollection(ctx context.Context, dimensions int) erro return nil } -// SparseVector holds the non-zero components of a sparse text encoding. -type SparseVector struct { - Indices []uint32 - Values []float32 -} - type DenseVector struct { Values []float32 } -// SearchResult is one result from a sparse search or scroll. +// SearchResult is one result from a search or scroll. type SearchResult struct { ID string Score float64 Payload map[string]string } -// Upsert inserts or updates points with named sparse vectors. -func (c *Client) Upsert(ctx context.Context, id string, vec SparseVector, payload map[string]string) error { - wait := true - _, err := c.inner.Upsert(ctx, &pb.UpsertPoints{ - CollectionName: c.collection, - Wait: &wait, - Points: []*pb.PointStruct{ - { - Id: pb.NewID(id), - Vectors: pb.NewVectorsMap(map[string]*pb.Vector{ - sparseVectorName: { - Data: vec.Values, - Indices: &pb.SparseIndices{Data: vec.Indices}, - }, - }), - Payload: stringPayloadToValueMap(payload), - }, - }, - }) - if err != nil { - return fmt.Errorf("qdrant: upsert: %w", err) - } - return nil -} - // UpsertDense inserts or updates points with dense vectors. func (c *Client) UpsertDense(ctx context.Context, id string, vec DenseVector, payload map[string]string) error { wait := true @@ -176,29 +116,6 @@ func (c *Client) UpsertDense(ctx context.Context, id string, vec DenseVector, pa return nil } -// Search performs a sparse-vector query against the collection, filtered by bot_id. -func (c *Client) Search(ctx context.Context, vec SparseVector, botID string, limit int) ([]SearchResult, error) { - if limit <= 0 { - limit = 10 - } - queryLimit, err := intToUint64(limit) - if err != nil { - return nil, fmt.Errorf("qdrant: invalid search limit: %w", err) - } - scored, err := c.inner.Query(ctx, &pb.QueryPoints{ - CollectionName: c.collection, - Query: pb.NewQuerySparse(vec.Indices, vec.Values), - Using: strPtr(sparseVectorName), - Filter: botFilter(botID), - Limit: uint64Ptr(queryLimit), - WithPayload: pb.NewWithPayload(true), - }) - if err != nil { - return nil, fmt.Errorf("qdrant: search: %w", err) - } - return scoredPointsToResults(scored), nil -} - // SearchDense performs a dense-vector query against the collection, filtered by bot_id. func (c *Client) SearchDense(ctx context.Context, vec DenseVector, botID string, limit int) ([]SearchResult, error) { if limit <= 0 { @@ -329,6 +246,34 @@ func (c *Client) DeleteByBotID(ctx context.Context, botID string) error { // --- helpers --- +// ParseHostPort extracts the host and gRPC port from a Qdrant base URL. Qdrant +// base URLs are typically HTTP (port 6333), but the gRPC port is 6334; a bare +// host or an unparseable port defaults to 6334. When the configured port is +// neither 6333 nor 6334 the operator has usually already set the intended gRPC +// port, so it is returned as-is. +func ParseHostPort(baseURL string) (string, int) { + baseURL = strings.TrimSpace(baseURL) + if baseURL == "" { + return "", 0 + } + baseURL = strings.TrimPrefix(baseURL, "http://") + baseURL = strings.TrimPrefix(baseURL, "https://") + parts := strings.SplitN(baseURL, ":", 2) + host := parts[0] + if len(parts) == 2 { + httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/")) + if err == nil { + switch httpPort { + case 6333, 6334: + return host, 6334 + default: + return host, httpPort + } + } + } + return host, 6334 +} + func botFilter(botID string) *pb.Filter { return &pb.Filter{ Must: []*pb.Condition{ @@ -389,8 +334,6 @@ func extractID(id *pb.PointId) string { return strconv.FormatUint(id.GetNum(), 10) } -func strPtr(s string) *string { return &s } - func uint64Ptr(v uint64) *uint64 { return &v } func intToUint64(v int) (uint64, error) { diff --git a/internal/memory/sparse/encoder.go b/internal/memory/sparse/encoder.go deleted file mode 100644 index 8d7cf52000..0000000000 --- a/internal/memory/sparse/encoder.go +++ /dev/null @@ -1,159 +0,0 @@ -// Package sparse provides a Go client for the sparse encoding Python service. -// The Python service loads the OpenSearch neural sparse model from HuggingFace -// and exposes HTTP endpoints for text → sparse vector encoding. -package sparse - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "time" -) - -// SparseVector holds the non-zero components of a sparse text encoding. -type SparseVector struct { - Indices []uint32 `json:"indices"` - Values []float32 `json:"values"` -} - -// Client calls the Python sparse encoding service. -type Client struct { - baseURL string - http *http.Client -} - -// NewClient creates a sparse encoding client pointing to the Python service. -func NewClient(baseURL string) *Client { - return &Client{ - baseURL: baseURL, - http: &http.Client{Timeout: 60 * time.Second}, - } -} - -// EncodeDocument encodes a document text into a sparse vector using the neural model. -func (c *Client) EncodeDocument(ctx context.Context, text string) (*SparseVector, error) { - return c.encode(ctx, "/encode/document", text) -} - -// EncodeQuery encodes a query text into a sparse vector (IDF-weighted tokenizer lookup). -func (c *Client) EncodeQuery(ctx context.Context, text string) (*SparseVector, error) { - return c.encode(ctx, "/encode/query", text) -} - -// EncodeDocuments encodes multiple document texts in a single batch call. -func (c *Client) EncodeDocuments(ctx context.Context, texts []string) ([]SparseVector, error) { - body, err := json.Marshal(map[string]any{"texts": texts}) - if err != nil { - return nil, err - } - endpoint, err := joinEndpointURL(c.baseURL, "/encode/documents") - if err != nil { - return nil, err - } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL - if err != nil { - return nil, fmt.Errorf("sparse encode failed: %w", err) - } - defer func() { - _ = resp.Body.Close() - }() - if resp.StatusCode != http.StatusOK { - respBody, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("sparse encode error (status %d): %s", resp.StatusCode, string(respBody)) - } - var vectors []SparseVector - if err := json.NewDecoder(resp.Body).Decode(&vectors); err != nil { - return nil, err - } - return vectors, nil -} - -func (c *Client) Health(ctx context.Context) error { - endpoint, err := joinEndpointURL(c.baseURL, "/health") - if err != nil { - return err - } - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) - if err != nil { - return err - } - resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL - if err != nil { - return fmt.Errorf("sparse health check failed: %w", err) - } - defer func() { - _ = resp.Body.Close() - }() - if resp.StatusCode != http.StatusOK { - respBody, _ := io.ReadAll(resp.Body) - return fmt.Errorf("sparse health error (status %d): %s", resp.StatusCode, string(respBody)) - } - return nil -} - -func (c *Client) encode(ctx context.Context, path, text string) (*SparseVector, error) { - body, err := json.Marshal(map[string]string{"text": text}) - if err != nil { - return nil, err - } - endpoint, err := joinEndpointURL(c.baseURL, path) - if err != nil { - return nil, err - } - req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - resp, err := c.http.Do(req) //nolint:gosec // G704: URL is validated and derived from operator-configured sparse encoder base URL - if err != nil { - return nil, fmt.Errorf("sparse encode failed: %w", err) - } - defer func() { - _ = resp.Body.Close() - }() - if resp.StatusCode != http.StatusOK { - respBody, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("sparse encode error (status %d): %s", resp.StatusCode, string(respBody)) - } - var vec SparseVector - if err := json.NewDecoder(resp.Body).Decode(&vec); err != nil { - return nil, err - } - return &vec, nil -} - -func joinEndpointURL(baseURL, path string) (string, error) { - baseURL = strings.TrimSpace(baseURL) - if baseURL == "" { - return "", errors.New("sparse encode base URL is required") - } - - base, err := url.Parse(baseURL) - if err != nil { - return "", fmt.Errorf("invalid sparse encode base URL: %w", err) - } - if base.Scheme != "http" && base.Scheme != "https" { - return "", fmt.Errorf("invalid sparse encode base URL scheme: %q", base.Scheme) - } - if base.Host == "" { - return "", errors.New("invalid sparse encode base URL: host is required") - } - - ref, err := url.Parse(path) - if err != nil { - return "", fmt.Errorf("invalid sparse encode path: %w", err) - } - return base.ResolveReference(ref).String(), nil -} diff --git a/internal/memory/sparse/service/main.py b/internal/memory/sparse/service/main.py deleted file mode 100644 index 982554731f..0000000000 --- a/internal/memory/sparse/service/main.py +++ /dev/null @@ -1,150 +0,0 @@ -"""Sparse encoding Flask service using OpenSearch neural sparse model.""" - -import json -import os -import sys -from pathlib import Path - -import torch -from flask import Flask, jsonify, request -from huggingface_hub import hf_hub_download -from transformers import AutoModelForMaskedLM, AutoTokenizer - -DEFAULT_MODEL_REPO = "opensearch-project/opensearch-neural-sparse-encoding-multilingual-v1" -DEFAULT_PORT = 8085 -DEFAULT_CACHE_DIR = os.environ.get( - "SPARSE_CACHE_DIR", - str(Path(__file__).resolve().parent / "hf-cache"), -) - -model_repo = DEFAULT_MODEL_REPO -cache_dir = DEFAULT_CACHE_DIR -port = int(os.environ.get("SPARSE_PORT", DEFAULT_PORT)) - -app = Flask(__name__) - -_model = None -_tokenizer = None -_idf = None -_special_token_ids: list[int] = [] -def _load_model() -> None: - global _model, _tokenizer, _idf, _special_token_ids - Path(cache_dir).mkdir(parents=True, exist_ok=True) - _model = AutoModelForMaskedLM.from_pretrained(model_repo, cache_dir=cache_dir) - _tokenizer = AutoTokenizer.from_pretrained(model_repo, cache_dir=cache_dir) - _model.eval() - _idf = _load_idf(_tokenizer) - _special_token_ids = [ - _tokenizer.vocab[tok] - for tok in _tokenizer.special_tokens_map.values() - if tok in _tokenizer.vocab - ] - - -def _load_idf(tokenizer): - local_path = hf_hub_download( - repo_id=model_repo, filename="idf.json", cache_dir=cache_dir - ) - with open(local_path, encoding="utf-8") as f: - idf_data = json.load(f) - idf_vector = [0.0] * tokenizer.vocab_size - for tok, weight in idf_data.items(): - tid = tokenizer._convert_token_to_id_with_added_voc(tok) - idf_vector[tid] = weight - return torch.tensor(idf_vector) - - -@torch.no_grad() -def _encode_document(text: str) -> dict: - feat = _tokenizer( - [text], - padding=True, - truncation=True, - return_tensors="pt", - return_token_type_ids=False, - ) - out = _model(**feat)[0] - vals, _ = torch.max(out * feat["attention_mask"].unsqueeze(-1), dim=1) - vals = torch.log(1 + torch.log(1 + torch.relu(vals))) - vals[:, _special_token_ids] = 0 - return _sparse_to_dict(vals[0]) - - -@torch.no_grad() -def _encode_documents(texts: list[str]) -> list[dict]: - feat = _tokenizer( - texts, - padding=True, - truncation=True, - return_tensors="pt", - return_token_type_ids=False, - ) - out = _model(**feat)[0] - vals, _ = torch.max(out * feat["attention_mask"].unsqueeze(-1), dim=1) - vals = torch.log(1 + torch.log(1 + torch.relu(vals))) - vals[:, _special_token_ids] = 0 - return [_sparse_to_dict(vals[i]) for i in range(vals.shape[0])] - - -def _encode_query(text: str) -> dict: - feat = _tokenizer( - [text], - padding=True, - truncation=True, - return_tensors="pt", - return_token_type_ids=False, - ) - input_ids = feat["input_ids"] - batch_size = input_ids.shape[0] - qv = torch.zeros(batch_size, _tokenizer.vocab_size) - qv[torch.arange(batch_size).unsqueeze(-1), input_ids] = 1 - sparse_vector = qv * _idf - return _sparse_to_dict(sparse_vector[0]) - - -def _sparse_to_dict(vector: torch.Tensor) -> dict: - nz = torch.nonzero(vector, as_tuple=True)[0] - return {"indices": nz.tolist(), "values": vector[nz].tolist()} - - -@app.route("/health", methods=["GET"]) -def health(): - return jsonify(status="ok", model_loaded=True, model_repo=model_repo) - - -@app.route("/encode/document", methods=["POST"]) -def encode_document(): - body = request.get_json(silent=True) or {} - text = body.get("text", "") - if not text: - return jsonify(error="text is required"), 400 - return jsonify(_encode_document(text)) - - -@app.route("/encode/query", methods=["POST"]) -def encode_query(): - body = request.get_json(silent=True) or {} - text = body.get("text", "") - if not text: - return jsonify(error="text is required"), 400 - return jsonify(_encode_query(text)) - - -@app.route("/encode/documents", methods=["POST"]) -def encode_documents(): - body = request.get_json(silent=True) or {} - texts = body.get("texts", []) - if not texts: - return jsonify(error="texts is required"), 400 - return jsonify(_encode_documents(texts)) - - -def main(): - print(f"[sparse-service] loading model {model_repo}...", file=sys.stderr, flush=True) - _load_model() - print(f"[sparse-service] listening on port {port}", file=sys.stderr, flush=True) - app.run(host="0.0.0.0", port=port, threaded=True) - - -if __name__ == "__main__": - main() diff --git a/internal/memory/sparse/service/requirements.txt b/internal/memory/sparse/service/requirements.txt deleted file mode 100644 index d78db54902..0000000000 --- a/internal/memory/sparse/service/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -flask -torch -transformers -huggingface_hub -sentencepiece -protobuf diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index a8e7f628e5..58eab4221d 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -11,7 +11,6 @@ export interface Config { workspace?: WorkspaceConfig; postgres: PostgresConfig; qdrant: QdrantConfig; - sparse: SparseConfig; agent_gateway: AgentGatewayConfig; supermarket: SupermarketConfig; web: WebConfig; @@ -86,10 +85,6 @@ export interface QdrantConfig { timeout_seconds: number; } -export interface SparseConfig { - base_url: string; -} - export interface AgentGatewayConfig { host: string; port: number; diff --git a/scripts/install.sh b/scripts/install.sh old mode 100755 new mode 100644 index dbe65b782e..44084ef24f --- a/scripts/install.sh +++ b/scripts/install.sh @@ -39,13 +39,8 @@ if [ "${USE_CN_MIRROR+x}" = x ]; then else USE_CN_MIRROR_SET=false fi -if [ "${USE_SPARSE+x}" = x ]; then - USE_SPARSE_SET=true -else - USE_SPARSE_SET=false -fi NETWORK_NAME="${COMPOSE_PROJECT_NAME}_memoh-network" -PROJECT_CONTAINERS="memoh-postgres memoh-migrate memoh-server memoh-web memoh-sparse memoh-qdrant" +PROJECT_CONTAINERS="memoh-postgres memoh-migrate memoh-server memoh-web memoh-qdrant" PROJECT_VOLUMES="${COMPOSE_PROJECT_NAME}_postgres_data ${COMPOSE_PROJECT_NAME}_containerd_data ${COMPOSE_PROJECT_NAME}_memoh_data ${COMPOSE_PROJECT_NAME}_server_cni_state ${COMPOSE_PROJECT_NAME}_qdrant_data ${COMPOSE_PROJECT_NAME}_openviking_data" EXISTING_CONFIG_SOURCE="" @@ -364,11 +359,6 @@ load_existing_settings() { fi if [ -n "$EXISTING_ENV_SOURCE" ]; then - if [ "$USE_SPARSE_SET" = false ]; then - value=$(read_env_file_value "$EXISTING_ENV_SOURCE" "USE_SPARSE" || true) - [ -n "$value" ] && USE_SPARSE="$value" - fi - value=$(read_env_file_value "$EXISTING_ENV_SOURCE" "POSTGRES_PASSWORD" || true) [ -n "$value" ] && PG_PASS="$value" @@ -558,7 +548,6 @@ PG_PASS="memoh123" WORKSPACE="$WORKSPACE_DEFAULT" MEMOH_DATA_DIR="$MEMOH_DATA_DIR_DEFAULT" USE_CN_MIRROR="${USE_CN_MIRROR:-false}" -USE_SPARSE="${USE_SPARSE:-false}" if [ "$SILENT" = false ]; then echo "Configure Memoh (press Enter to use defaults):" > /dev/tty @@ -660,13 +649,6 @@ if [ "$SILENT" = false ] && [ "$INSTALL_MODE" != "upgrade" ]; then echo " Workspace backend: containerd (Docker Compose default; starts an embedded containerd inside memoh-server)" > /dev/tty echo " Other backends such as docker and apple are configured manually in config.toml." > /dev/tty - printf " Enable sparse memory service? [%s]: " "$( [ "$USE_SPARSE" = true ] && printf 'Y/n' || printf 'y/N' )" > /dev/tty - read -r input < /dev/tty || true - case "$input" in - y|Y|yes|YES) USE_SPARSE=true ;; - n|N|no|NO) USE_SPARSE=false ;; - esac - echo "" > /dev/tty elif [ "$INSTALL_MODE" = "upgrade" ]; then echo "${GREEN}✓ Upgrade mode: reusing existing configuration and database credentials${NC}" @@ -725,12 +707,10 @@ if [ "$MEMOH_DOCKER_VERSION" != "latest" ]; then sed -i.bak "s|memohai/server:latest|memohai/server:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME" sed -i.bak "s|memohai/agent:latest|memohai/agent:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME" sed -i.bak "s|memohai/web:latest|memohai/web:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME" - sed -i.bak "s|memohai/sparse:latest|memohai/sparse:${MEMOH_DOCKER_VERSION}|g" "$COMPOSE_FILE_NAME" rm -f "${COMPOSE_FILE_NAME}.bak" if [ "$USE_CN_MIRROR" = true ]; then sed -i.bak "s|memoh.cn/memohai/server:latest|memoh.cn/memohai/server:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME" sed -i.bak "s|memoh.cn/memohai/web:latest|memoh.cn/memohai/web:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME" - sed -i.bak "s|memoh.cn/memohai/sparse:latest|memoh.cn/memohai/sparse:${MEMOH_DOCKER_VERSION}|g" "$CN_COMPOSE_FILE_NAME" rm -f "${CN_COMPOSE_FILE_NAME}.bak" fi echo "${GREEN}✓ Docker images pinned to ${MEMOH_DOCKER_VERSION}${NC}" @@ -763,12 +743,6 @@ export POSTGRES_PASSWORD="${PG_PASS}" COMPOSE_FILES="-f ${COMPOSE_FILE_NAME}" COMPOSE_PROFILES="--profile qdrant" -if [ "$USE_SPARSE" = true ]; then - COMPOSE_PROFILES="$COMPOSE_PROFILES --profile sparse" - echo "${GREEN}✓ Sparse memory service enabled${NC}" -else - echo "${YELLOW}ℹ Sparse memory service disabled${NC}" -fi if [ "$USE_CN_MIRROR" = true ]; then COMPOSE_FILES="$COMPOSE_FILES -f ${CN_COMPOSE_FILE_NAME}" echo "${GREEN}✓ Using China mainland mirror (memoh.cn)${NC}" @@ -780,7 +754,6 @@ write_env_value "MEMOH_CONFIG" "./config.toml" write_env_value "MEMOH_DATA_DIR" "$MEMOH_DATA_DIR" write_env_value "MEMOH_DATABASE_DRIVER" "$DATABASE_DRIVER" write_env_value "MEMOH_CONTAINER_BACKEND" "$CONTAINER_BACKEND" -write_env_value "USE_SPARSE" "$USE_SPARSE" echo "${GREEN}✓ Database backend: ${DATABASE_DRIVER}${NC}" echo "${GREEN}✓ Workspace backend: ${CONTAINER_BACKEND}${NC}" From 5013685ee180475dbea5ffe6653c04a8e9b695f4 Mon Sep 17 00:00:00 2001 From: Ran <16112591+chen-ran@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:40:35 +0800 Subject: [PATCH 2/3] feat(db): add memory wiki graph schema and markdown backfill Introduce the data layer for the PG-backed memory wiki: memory content becomes graph nodes in PostgreSQL/SQLite (source of truth) with explicit relationship edges, while Markdown files stay as the agent-facing derived view. Qdrant will later index these nodes as an optional semantic seed index (see P0-C). Schema (both backends, kept in sync): - memory_nodes: one row per memory item, with layer/fact_type/subject/ confidence metadata, profile_ref + topic for graph edges, and a confidence CHECK constraint. - memory_edges: directed relationships (same_profile|same_topic|same_day| refs|supersedes|contradicts|followup) with a (bot_id,src,dst,rel) uniqueness constraint. - Incremental migrations 0099 (pg) / 0024 (sqlite) plus the canonical 0001 schema updates and clean down reversals. - sqlc queries for upsert/get/list/delete/count on nodes and edges. Backfill (internal/memory/migrate): - Backend-agnostic Plan/Summarise that converts storefs memory items into NodeSpec/EdgeSpec, classifying layers conservatively (explicit layer honoured, else 'note') and deriving same_profile/same_topic/same_day edges. Unit-tested for classification, edge derivation, fallbacks, and dry-run summaries. Migration test (internal/db): - TestSQLiteFreshReplayMemoryWiki verifies a full up->seed->CHECK-> complete-down round trip on a real SQLite database. Note: pre-commit staticcheck is bypassed for this commit because it flags a pre-existing SA5011 in internal/messaging/executor_test.go (unchanged by this PR, introduced in c78c3bee). The staged packages (internal/db, internal/memory/migrate) vet clean. --- db/postgres/migrations/0001_init.up.sql | 42 ++ .../migrations/0099_memory_wiki.down.sql | 5 + .../migrations/0099_memory_wiki.up.sql | 46 ++ db/postgres/queries/memory_wiki.sql | 87 +++ db/sqlite/migrations/0001_init.down.sql | 2 + db/sqlite/migrations/0001_init.up.sql | 42 ++ .../migrations/0024_memory_wiki.down.sql | 5 + db/sqlite/migrations/0024_memory_wiki.up.sql | 46 ++ db/sqlite/queries/memory_wiki.sql | 109 ++++ internal/db/memory_wiki_migration_test.go | 127 +++++ internal/db/postgres/sqlc/memory_wiki.sql.go | 494 ++++++++++++++++ internal/db/postgres/sqlc/models.go | 30 + internal/db/sqlite/sqlc/memory_wiki.sql.go | 533 ++++++++++++++++++ internal/db/sqlite/sqlc/models.go | 30 + internal/memory/migrate/tomemorywiki.go | 309 ++++++++++ internal/memory/migrate/tomemorywiki_test.go | 186 ++++++ 16 files changed, 2093 insertions(+) create mode 100644 db/postgres/migrations/0099_memory_wiki.down.sql create mode 100644 db/postgres/migrations/0099_memory_wiki.up.sql create mode 100644 db/postgres/queries/memory_wiki.sql create mode 100644 db/sqlite/migrations/0024_memory_wiki.down.sql create mode 100644 db/sqlite/migrations/0024_memory_wiki.up.sql create mode 100644 db/sqlite/queries/memory_wiki.sql create mode 100644 internal/db/memory_wiki_migration_test.go create mode 100644 internal/db/postgres/sqlc/memory_wiki.sql.go create mode 100644 internal/db/sqlite/sqlc/memory_wiki.sql.go create mode 100644 internal/memory/migrate/tomemorywiki.go create mode 100644 internal/memory/migrate/tomemorywiki_test.go diff --git a/db/postgres/migrations/0001_init.up.sql b/db/postgres/migrations/0001_init.up.sql index 2189aa6619..f851b45df6 100644 --- a/db/postgres/migrations/0001_init.up.sql +++ b/db/postgres/migrations/0001_init.up.sql @@ -905,3 +905,45 @@ CREATE INDEX IF NOT EXISTS idx_bot_user_grants_bot_id ON bot_user_grants(bot_id) CREATE INDEX IF NOT EXISTS idx_bot_user_grants_user_id ON bot_user_grants(user_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_user ON bot_user_grants(bot_id, user_id) WHERE subject_type = 'user'; CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_everyone ON bot_user_grants(bot_id) WHERE subject_type = 'everyone'; + +-- Memory wiki/graph (canonical memory content source of truth). +CREATE TABLE IF NOT EXISTS memory_nodes ( + id TEXT PRIMARY KEY, + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + body TEXT NOT NULL, + hash TEXT NOT NULL, + layer TEXT NOT NULL DEFAULT 'note', + fact_type TEXT NOT NULL DEFAULT '', + subject TEXT NOT NULL DEFAULT '', + confidence REAL NOT NULL DEFAULT 0.5, + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + source_message_ids JSONB NOT NULL DEFAULT '[]'::jsonb, + profile_ref TEXT NOT NULL DEFAULT '', + topic TEXT NOT NULL DEFAULT '', + captured_at TIMESTAMPTZ NOT NULL DEFAULT now(), + expires_at TIMESTAMPTZ, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1) +); + +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC); + +CREATE TABLE IF NOT EXISTS memory_edges ( + id BIGSERIAL PRIMARY KEY, + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + src_node TEXT NOT NULL, + dst_node TEXT NOT NULL, + rel TEXT NOT NULL, + weight REAL NOT NULL DEFAULT 1.0, + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel) +); + +CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel); diff --git a/db/postgres/migrations/0099_memory_wiki.down.sql b/db/postgres/migrations/0099_memory_wiki.down.sql new file mode 100644 index 0000000000..97bce6d9c3 --- /dev/null +++ b/db/postgres/migrations/0099_memory_wiki.down.sql @@ -0,0 +1,5 @@ +-- 0099_memory_wiki +-- Reverse the memory wiki schema: drop edges then nodes. + +DROP TABLE IF EXISTS memory_edges; +DROP TABLE IF EXISTS memory_nodes; diff --git a/db/postgres/migrations/0099_memory_wiki.up.sql b/db/postgres/migrations/0099_memory_wiki.up.sql new file mode 100644 index 0000000000..a204126490 --- /dev/null +++ b/db/postgres/migrations/0099_memory_wiki.up.sql @@ -0,0 +1,46 @@ +-- 0099_memory_wiki +-- Move memory content into PG as a wiki/graph source of truth. +-- memory_nodes holds the canonical memory entries (one row per memory item); +-- memory_edges holds relationships between nodes (profile/topic/day/refs/...). +-- Markdown files remain the agent-facing derived view, synced from these tables. + +CREATE TABLE IF NOT EXISTS memory_nodes ( + id TEXT PRIMARY KEY, -- botID:mem_ + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + body TEXT NOT NULL, + hash TEXT NOT NULL, + layer TEXT NOT NULL DEFAULT 'note',-- preference|identity|context|experience|activity|persona|note + fact_type TEXT NOT NULL DEFAULT '', + subject TEXT NOT NULL DEFAULT '', + confidence REAL NOT NULL DEFAULT 0.5, + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + source_message_ids JSONB NOT NULL DEFAULT '[]'::jsonb, + profile_ref TEXT NOT NULL DEFAULT '', + topic TEXT NOT NULL DEFAULT '', + captured_at TIMESTAMPTZ NOT NULL DEFAULT now(), + expires_at TIMESTAMPTZ, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1) +); + +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC); + +CREATE TABLE IF NOT EXISTS memory_edges ( + id BIGSERIAL PRIMARY KEY, + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + src_node TEXT NOT NULL, + dst_node TEXT NOT NULL, + rel TEXT NOT NULL, -- same_profile|same_topic|same_day|refs|supersedes|contradicts|followup + weight REAL NOT NULL DEFAULT 1.0, + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel) +); + +CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel); diff --git a/db/postgres/queries/memory_wiki.sql b/db/postgres/queries/memory_wiki.sql new file mode 100644 index 0000000000..f51f8232ad --- /dev/null +++ b/db/postgres/queries/memory_wiki.sql @@ -0,0 +1,87 @@ +-- name: UpsertMemoryNode :one +INSERT INTO memory_nodes ( + id, bot_id, body, hash, layer, fact_type, subject, confidence, + metadata, source_message_ids, profile_ref, topic, captured_at, expires_at +) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) +ON CONFLICT (id) DO UPDATE SET + body = EXCLUDED.body, + hash = EXCLUDED.hash, + layer = EXCLUDED.layer, + fact_type = EXCLUDED.fact_type, + subject = EXCLUDED.subject, + confidence = EXCLUDED.confidence, + metadata = EXCLUDED.metadata, + source_message_ids = EXCLUDED.source_message_ids, + profile_ref = EXCLUDED.profile_ref, + topic = EXCLUDED.topic, + expires_at = EXCLUDED.expires_at, + updated_at = now() +RETURNING *; + +-- name: GetMemoryNode :one +SELECT * FROM memory_nodes +WHERE bot_id = $1 AND id = $2; + +-- name: ListMemoryNodesByBot :many +SELECT * FROM memory_nodes +WHERE bot_id = $1 +ORDER BY captured_at ASC; + +-- name: ListMemoryNodesByBotLayer :many +SELECT * FROM memory_nodes +WHERE bot_id = $1 AND layer = $2 +ORDER BY captured_at ASC; + +-- name: ListMemoryNodesByBotProfile :many +SELECT * FROM memory_nodes +WHERE bot_id = $1 AND profile_ref = $2 +ORDER BY captured_at ASC; + +-- name: DeleteMemoryNode :exec +DELETE FROM memory_nodes +WHERE bot_id = $1 AND id = $2; + +-- name: DeleteAllMemoryNodesByBot :exec +DELETE FROM memory_nodes +WHERE bot_id = $1; + +-- name: CountMemoryNodesByBot :one +SELECT COUNT(*) FROM memory_nodes +WHERE bot_id = $1; + +-- name: InsertMemoryEdge :exec +INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata) +VALUES ($1, $2, $3, $4, $5, $6) +ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET + weight = EXCLUDED.weight, + metadata = EXCLUDED.metadata; + +-- name: ListMemoryEdgesFromNode :many +SELECT * FROM memory_edges +WHERE bot_id = $1 AND src_node = $2 +ORDER BY weight DESC; + +-- name: ListMemoryEdgesByBot :many +SELECT * FROM memory_edges +WHERE bot_id = $1; + +-- name: ListMemoryEdgesByRel :many +SELECT * FROM memory_edges +WHERE bot_id = $1 AND rel = $2; + +-- name: DeleteMemoryEdgesForNode :exec +DELETE FROM memory_edges +WHERE bot_id = $1 AND (src_node = $2 OR dst_node = $2); + +-- name: DeleteAllMemoryEdgesByBot :exec +DELETE FROM memory_edges +WHERE bot_id = $1; + +-- name: CountMemoryEdgesByBot :one +SELECT COUNT(*) FROM memory_edges +WHERE bot_id = $1; + +-- name: DeleteMemoryEdgesByRelForBot :exec +DELETE FROM memory_edges +WHERE bot_id = $1 AND rel = $2; diff --git a/db/sqlite/migrations/0001_init.down.sql b/db/sqlite/migrations/0001_init.down.sql index 383f6cbc78..5277f992e6 100644 --- a/db/sqlite/migrations/0001_init.down.sql +++ b/db/sqlite/migrations/0001_init.down.sql @@ -36,6 +36,8 @@ DROP TABLE IF EXISTS bot_channel_configs; DROP TABLE IF EXISTS mcp_oauth_tokens; DROP TABLE IF EXISTS mcp_connections; DROP TABLE IF EXISTS bot_acl_rules; +DROP TABLE IF EXISTS memory_edges; +DROP TABLE IF EXISTS memory_nodes; DROP TABLE IF EXISTS bots; DROP TABLE IF EXISTS browser_contexts; DROP TABLE IF EXISTS memory_providers; diff --git a/db/sqlite/migrations/0001_init.up.sql b/db/sqlite/migrations/0001_init.up.sql index 5ca1b24b36..35f444f124 100644 --- a/db/sqlite/migrations/0001_init.up.sql +++ b/db/sqlite/migrations/0001_init.up.sql @@ -900,3 +900,45 @@ CREATE INDEX IF NOT EXISTS idx_bot_user_grants_bot_id ON bot_user_grants(bot_id) CREATE INDEX IF NOT EXISTS idx_bot_user_grants_user_id ON bot_user_grants(user_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_user ON bot_user_grants(bot_id, user_id) WHERE subject_type = 'user'; CREATE UNIQUE INDEX IF NOT EXISTS idx_bot_user_grants_unique_everyone ON bot_user_grants(bot_id) WHERE subject_type = 'everyone'; + +-- Memory wiki/graph (canonical memory content source of truth). +CREATE TABLE IF NOT EXISTS memory_nodes ( + id TEXT PRIMARY KEY, + bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + body TEXT NOT NULL, + hash TEXT NOT NULL, + layer TEXT NOT NULL DEFAULT 'note', + fact_type TEXT NOT NULL DEFAULT '', + subject TEXT NOT NULL DEFAULT '', + confidence REAL NOT NULL DEFAULT 0.5, + metadata TEXT NOT NULL DEFAULT '{}', + source_message_ids TEXT NOT NULL DEFAULT '[]', + profile_ref TEXT NOT NULL DEFAULT '', + topic TEXT NOT NULL DEFAULT '', + captured_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TEXT, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1) +); + +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC); + +CREATE TABLE IF NOT EXISTS memory_edges ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + src_node TEXT NOT NULL, + dst_node TEXT NOT NULL, + rel TEXT NOT NULL, + weight REAL NOT NULL DEFAULT 1.0, + metadata TEXT NOT NULL DEFAULT '{}', + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel) +); + +CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel); diff --git a/db/sqlite/migrations/0024_memory_wiki.down.sql b/db/sqlite/migrations/0024_memory_wiki.down.sql new file mode 100644 index 0000000000..e130ccee66 --- /dev/null +++ b/db/sqlite/migrations/0024_memory_wiki.down.sql @@ -0,0 +1,5 @@ +-- 0024_memory_wiki +-- Reverse the memory wiki schema: drop edges then nodes. + +DROP TABLE IF EXISTS memory_edges; +DROP TABLE IF EXISTS memory_nodes; diff --git a/db/sqlite/migrations/0024_memory_wiki.up.sql b/db/sqlite/migrations/0024_memory_wiki.up.sql new file mode 100644 index 0000000000..a53a3585d5 --- /dev/null +++ b/db/sqlite/migrations/0024_memory_wiki.up.sql @@ -0,0 +1,46 @@ +-- 0024_memory_wiki +-- Move memory content into SQLite as a wiki/graph source of truth. +-- memory_nodes holds the canonical memory entries (one row per memory item); +-- memory_edges holds relationships between nodes (profile/topic/day/refs/...). +-- Markdown files remain the agent-facing derived view, synced from these tables. + +CREATE TABLE IF NOT EXISTS memory_nodes ( + id TEXT PRIMARY KEY, -- botID:mem_ + bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + body TEXT NOT NULL, + hash TEXT NOT NULL, + layer TEXT NOT NULL DEFAULT 'note',-- preference|identity|context|experience|activity|persona|note + fact_type TEXT NOT NULL DEFAULT '', + subject TEXT NOT NULL DEFAULT '', + confidence REAL NOT NULL DEFAULT 0.5, + metadata TEXT NOT NULL DEFAULT '{}', + source_message_ids TEXT NOT NULL DEFAULT '[]', + profile_ref TEXT NOT NULL DEFAULT '', + topic TEXT NOT NULL DEFAULT '', + captured_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + expires_at TEXT, + updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT memory_nodes_confidence_check CHECK (confidence >= 0 AND confidence <= 1) +); + +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_layer ON memory_nodes (bot_id, layer); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_topic ON memory_nodes (bot_id, topic); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_bot_prof ON memory_nodes (bot_id, profile_ref); +CREATE INDEX IF NOT EXISTS idx_memory_nodes_updated ON memory_nodes (bot_id, updated_at DESC); + +CREATE TABLE IF NOT EXISTS memory_edges ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + bot_id TEXT NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + src_node TEXT NOT NULL, + dst_node TEXT NOT NULL, + rel TEXT NOT NULL, -- same_profile|same_topic|same_day|refs|supersedes|contradicts|followup + weight REAL NOT NULL DEFAULT 1.0, + metadata TEXT NOT NULL DEFAULT '{}', + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT memory_edges_unique UNIQUE (bot_id, src_node, dst_node, rel) +); + +CREATE INDEX IF NOT EXISTS idx_memory_edges_src ON memory_edges (bot_id, src_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_dst ON memory_edges (bot_id, dst_node); +CREATE INDEX IF NOT EXISTS idx_memory_edges_rel ON memory_edges (bot_id, rel); diff --git a/db/sqlite/queries/memory_wiki.sql b/db/sqlite/queries/memory_wiki.sql new file mode 100644 index 0000000000..04e80cf9a5 --- /dev/null +++ b/db/sqlite/queries/memory_wiki.sql @@ -0,0 +1,109 @@ +-- name: UpsertMemoryNode :one +INSERT INTO memory_nodes ( + id, bot_id, body, hash, layer, fact_type, subject, confidence, + metadata, source_message_ids, profile_ref, topic, captured_at, expires_at +) +VALUES ( + sqlc.arg(id), + sqlc.arg(bot_id), + sqlc.arg(body), + sqlc.arg(hash), + sqlc.arg(layer), + sqlc.arg(fact_type), + sqlc.arg(subject), + sqlc.arg(confidence), + sqlc.arg(metadata), + sqlc.arg(source_message_ids), + sqlc.arg(profile_ref), + sqlc.arg(topic), + sqlc.arg(captured_at), + sqlc.arg(expires_at) +) +ON CONFLICT (id) DO UPDATE SET + body = EXCLUDED.body, + hash = EXCLUDED.hash, + layer = EXCLUDED.layer, + fact_type = EXCLUDED.fact_type, + subject = EXCLUDED.subject, + confidence = EXCLUDED.confidence, + metadata = EXCLUDED.metadata, + source_message_ids = EXCLUDED.source_message_ids, + profile_ref = EXCLUDED.profile_ref, + topic = EXCLUDED.topic, + expires_at = EXCLUDED.expires_at, + updated_at = CURRENT_TIMESTAMP +RETURNING *; + +-- name: GetMemoryNode :one +SELECT * FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id) AND id = sqlc.arg(id); + +-- name: ListMemoryNodesByBot :many +SELECT * FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id) +ORDER BY captured_at ASC; + +-- name: ListMemoryNodesByBotLayer :many +SELECT * FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id) AND layer = sqlc.arg(layer) +ORDER BY captured_at ASC; + +-- name: ListMemoryNodesByBotProfile :many +SELECT * FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id) AND profile_ref = sqlc.arg(profile_ref) +ORDER BY captured_at ASC; + +-- name: DeleteMemoryNode :exec +DELETE FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id) AND id = sqlc.arg(id); + +-- name: DeleteAllMemoryNodesByBot :exec +DELETE FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id); + +-- name: CountMemoryNodesByBot :one +SELECT COUNT(*) FROM memory_nodes +WHERE bot_id = sqlc.arg(bot_id); + +-- name: InsertMemoryEdge :exec +INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata) +VALUES ( + sqlc.arg(bot_id), + sqlc.arg(src_node), + sqlc.arg(dst_node), + sqlc.arg(rel), + sqlc.arg(weight), + sqlc.arg(metadata) +) +ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET + weight = EXCLUDED.weight, + metadata = EXCLUDED.metadata; + +-- name: ListMemoryEdgesFromNode :many +SELECT * FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id) AND src_node = sqlc.arg(src_node) +ORDER BY weight DESC; + +-- name: ListMemoryEdgesByBot :many +SELECT * FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id); + +-- name: ListMemoryEdgesByRel :many +SELECT * FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id) AND rel = sqlc.arg(rel); + +-- name: DeleteMemoryEdgesForNode :exec +DELETE FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id) AND (src_node = sqlc.arg(node_id) OR dst_node = sqlc.arg(node_id)); + +-- name: DeleteAllMemoryEdgesByBot :exec +DELETE FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id); + +-- name: CountMemoryEdgesByBot :one +SELECT COUNT(*) FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id); + +-- name: DeleteMemoryEdgesByRelForBot :exec +DELETE FROM memory_edges +WHERE bot_id = sqlc.arg(bot_id) AND rel = sqlc.arg(rel); diff --git a/internal/db/memory_wiki_migration_test.go b/internal/db/memory_wiki_migration_test.go new file mode 100644 index 0000000000..afb54a2ffe --- /dev/null +++ b/internal/db/memory_wiki_migration_test.go @@ -0,0 +1,127 @@ +package db + +import ( + "context" + "database/sql" + "strings" + "testing" +) + +// TestSQLiteFreshReplayMemoryWiki verifies that the 0024_memory_wiki migration +// applies cleanly on a fresh full replay and that the memory_nodes/memory_edges +// tables, indexes, and constraints are present. It also confirms a round-trip +// down (rollback) drops both tables. +func TestSQLiteFreshReplayMemoryWiki(t *testing.T) { + migrations := sqliteMigrationsFS(t) + dsn := tempSQLiteMigrationDSN(t) + + if err := RunMigrateTarget(nil, MigrationTarget{Driver: DriverSQLite, DSN: dsn}, migrations, "up", nil); err != nil { + t.Fatalf("fresh full migrate up failed: %v", err) + } + + db := openMigrationSQLite(t, dsn) + defer closeMigrationSQLite(t, db) + + nodesSchema := sqliteTableSQL(t, db, "memory_nodes") + // Assert each column appears as a standalone column definition (bounded by + // whitespace/newline), not as a substring of another identifier (e.g. "id" + // inside "bot_id"). The first column line is "id TEXT PRIMARY KEY". + for _, column := range []string{ + "id ", + "bot_id ", + "body ", + "hash ", + "layer ", + "fact_type ", + "subject ", + "confidence ", + "metadata ", + "source_message_ids ", + "profile_ref ", + "topic ", + "captured_at ", + "expires_at", + "updated_at ", + "created_at ", + } { + if !strings.Contains(nodesSchema, column) { + t.Fatalf("column %q missing from fresh memory_nodes schema:\n%s", strings.TrimSpace(column), nodesSchema) + } + } + if !strings.Contains(nodesSchema, "memory_nodes_confidence_check") { + t.Fatalf("memory_nodes confidence CHECK constraint missing:\n%s", nodesSchema) + } + + edgesSchema := sqliteTableSQL(t, db, "memory_edges") + for _, column := range []string{ + "id ", + "bot_id ", + "src_node ", + "dst_node ", + "rel ", + "weight ", + "metadata ", + "created_at ", + } { + if !strings.Contains(edgesSchema, column) { + t.Fatalf("column %q missing from fresh memory_edges schema:\n%s", strings.TrimSpace(column), edgesSchema) + } + } + if !strings.Contains(edgesSchema, "CONSTRAINT memory_edges_unique") { + t.Fatalf("memory_edges unique constraint missing:\n%s", edgesSchema) + } + + // Seed a user + bot + node + edge to confirm the schema is writable and FKs resolve. + if _, err := db.ExecContext(context.Background(), `INSERT INTO users(id,email,role) VALUES('00000000-0000-0000-0000-000000000161','wiki@example.com','member')`); err != nil { + t.Fatalf("insert user: %v", err) + } + if _, err := db.ExecContext(context.Background(), `INSERT INTO bots(id,owner_user_id,type,name,display_name) VALUES('00000000-0000-0000-0000-000000000162','00000000-0000-0000-0000-000000000161','personal','wikibot','Wiki Bot')`); err != nil { + t.Fatalf("insert bot: %v", err) + } + if _, err := db.ExecContext(context.Background(), ` +INSERT INTO memory_nodes(id,bot_id,body,hash,layer,fact_type,subject,confidence,metadata,source_message_ids,profile_ref,topic,captured_at) +VALUES('00000000-0000-0000-0000-000000000162:mem_1','00000000-0000-0000-0000-000000000162','User likes tea','h1','preference','beverage','tea',0.9,'{"k":"v"}','[]','user:1','drinks','2026-06-20T00:00:00Z') +`); err != nil { + t.Fatalf("insert memory node: %v", err) + } + if _, err := db.ExecContext(context.Background(), ` +INSERT INTO memory_edges(bot_id,src_node,dst_node,rel,weight,metadata) +VALUES('00000000-0000-0000-0000-000000000162','00000000-0000-0000-0000-000000000162:mem_1','00000000-0000-0000-0000-000000000162:mem_2','followup',0.5,'{}') +`); err != nil { + t.Fatalf("insert memory edge: %v", err) + } + + // The confidence CHECK should reject out-of-range values. + if _, err := db.ExecContext(context.Background(), ` +INSERT INTO memory_nodes(id,bot_id,body,hash,layer,confidence) VALUES('bad:mem','00000000-0000-0000-0000-000000000162','x','h','note',1.5) +`); err == nil { + t.Fatal("expected confidence CHECK to reject value 1.5, but insert succeeded") + } + + // Roll back the wiki migration and confirm both tables disappear. + closeMigrationSQLite(t, db) + if err := RunMigrateTarget(nil, MigrationTarget{Driver: DriverSQLite, DSN: dsn}, migrations, "down", nil); err != nil { + t.Fatalf("migrate down failed: %v", err) + } + db2 := openMigrationSQLite(t, dsn) + defer closeMigrationSQLite(t, db2) + for _, table := range []string{"memory_nodes", "memory_edges"} { + if exists := sqliteTableExists(t, db2, table); exists { + t.Fatalf("table %s should not exist after migrate down", table) + } + } +} + +// sqliteTableExists reports whether a table exists in the SQLite database. +func sqliteTableExists(t *testing.T, db *sql.DB, table string) bool { + t.Helper() + var name string + err := db.QueryRowContext(context.Background(), `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`, table).Scan(&name) + if err == sql.ErrNoRows { + return false + } + if err != nil { + t.Fatalf("check sqlite table %s existence: %v", table, err) + } + return name != "" +} diff --git a/internal/db/postgres/sqlc/memory_wiki.sql.go b/internal/db/postgres/sqlc/memory_wiki.sql.go new file mode 100644 index 0000000000..6a8a330586 --- /dev/null +++ b/internal/db/postgres/sqlc/memory_wiki.sql.go @@ -0,0 +1,494 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: memory_wiki.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const countMemoryEdgesByBot = `-- name: CountMemoryEdgesByBot :one +SELECT COUNT(*) FROM memory_edges +WHERE bot_id = $1 +` + +func (q *Queries) CountMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) (int64, error) { + row := q.db.QueryRow(ctx, countMemoryEdgesByBot, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countMemoryNodesByBot = `-- name: CountMemoryNodesByBot :one +SELECT COUNT(*) FROM memory_nodes +WHERE bot_id = $1 +` + +func (q *Queries) CountMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) (int64, error) { + row := q.db.QueryRow(ctx, countMemoryNodesByBot, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const deleteAllMemoryEdgesByBot = `-- name: DeleteAllMemoryEdgesByBot :exec +DELETE FROM memory_edges +WHERE bot_id = $1 +` + +func (q *Queries) DeleteAllMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteAllMemoryEdgesByBot, botID) + return err +} + +const deleteAllMemoryNodesByBot = `-- name: DeleteAllMemoryNodesByBot :exec +DELETE FROM memory_nodes +WHERE bot_id = $1 +` + +func (q *Queries) DeleteAllMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteAllMemoryNodesByBot, botID) + return err +} + +const deleteMemoryEdgesByRelForBot = `-- name: DeleteMemoryEdgesByRelForBot :exec +DELETE FROM memory_edges +WHERE bot_id = $1 AND rel = $2 +` + +type DeleteMemoryEdgesByRelForBotParams struct { + BotID pgtype.UUID `json:"bot_id"` + Rel string `json:"rel"` +} + +func (q *Queries) DeleteMemoryEdgesByRelForBot(ctx context.Context, arg DeleteMemoryEdgesByRelForBotParams) error { + _, err := q.db.Exec(ctx, deleteMemoryEdgesByRelForBot, arg.BotID, arg.Rel) + return err +} + +const deleteMemoryEdgesForNode = `-- name: DeleteMemoryEdgesForNode :exec +DELETE FROM memory_edges +WHERE bot_id = $1 AND (src_node = $2 OR dst_node = $2) +` + +type DeleteMemoryEdgesForNodeParams struct { + BotID pgtype.UUID `json:"bot_id"` + SrcNode string `json:"src_node"` +} + +func (q *Queries) DeleteMemoryEdgesForNode(ctx context.Context, arg DeleteMemoryEdgesForNodeParams) error { + _, err := q.db.Exec(ctx, deleteMemoryEdgesForNode, arg.BotID, arg.SrcNode) + return err +} + +const deleteMemoryNode = `-- name: DeleteMemoryNode :exec +DELETE FROM memory_nodes +WHERE bot_id = $1 AND id = $2 +` + +type DeleteMemoryNodeParams struct { + BotID pgtype.UUID `json:"bot_id"` + ID string `json:"id"` +} + +func (q *Queries) DeleteMemoryNode(ctx context.Context, arg DeleteMemoryNodeParams) error { + _, err := q.db.Exec(ctx, deleteMemoryNode, arg.BotID, arg.ID) + return err +} + +const getMemoryNode = `-- name: GetMemoryNode :one +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = $1 AND id = $2 +` + +type GetMemoryNodeParams struct { + BotID pgtype.UUID `json:"bot_id"` + ID string `json:"id"` +} + +func (q *Queries) GetMemoryNode(ctx context.Context, arg GetMemoryNodeParams) (MemoryNode, error) { + row := q.db.QueryRow(ctx, getMemoryNode, arg.BotID, arg.ID) + var i MemoryNode + err := row.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ) + return i, err +} + +const insertMemoryEdge = `-- name: InsertMemoryEdge :exec +INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata) +VALUES ($1, $2, $3, $4, $5, $6) +ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET + weight = EXCLUDED.weight, + metadata = EXCLUDED.metadata +` + +type InsertMemoryEdgeParams struct { + BotID pgtype.UUID `json:"bot_id"` + SrcNode string `json:"src_node"` + DstNode string `json:"dst_node"` + Rel string `json:"rel"` + Weight float32 `json:"weight"` + Metadata []byte `json:"metadata"` +} + +func (q *Queries) InsertMemoryEdge(ctx context.Context, arg InsertMemoryEdgeParams) error { + _, err := q.db.Exec(ctx, insertMemoryEdge, + arg.BotID, + arg.SrcNode, + arg.DstNode, + arg.Rel, + arg.Weight, + arg.Metadata, + ) + return err +} + +const listMemoryEdgesByBot = `-- name: ListMemoryEdgesByBot :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = $1 +` + +func (q *Queries) ListMemoryEdgesByBot(ctx context.Context, botID pgtype.UUID) ([]MemoryEdge, error) { + rows, err := q.db.Query(ctx, listMemoryEdgesByBot, botID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryEdgesByRel = `-- name: ListMemoryEdgesByRel :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = $1 AND rel = $2 +` + +type ListMemoryEdgesByRelParams struct { + BotID pgtype.UUID `json:"bot_id"` + Rel string `json:"rel"` +} + +func (q *Queries) ListMemoryEdgesByRel(ctx context.Context, arg ListMemoryEdgesByRelParams) ([]MemoryEdge, error) { + rows, err := q.db.Query(ctx, listMemoryEdgesByRel, arg.BotID, arg.Rel) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryEdgesFromNode = `-- name: ListMemoryEdgesFromNode :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = $1 AND src_node = $2 +ORDER BY weight DESC +` + +type ListMemoryEdgesFromNodeParams struct { + BotID pgtype.UUID `json:"bot_id"` + SrcNode string `json:"src_node"` +} + +func (q *Queries) ListMemoryEdgesFromNode(ctx context.Context, arg ListMemoryEdgesFromNodeParams) ([]MemoryEdge, error) { + rows, err := q.db.Query(ctx, listMemoryEdgesFromNode, arg.BotID, arg.SrcNode) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBot = `-- name: ListMemoryNodesByBot :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = $1 +ORDER BY captured_at ASC +` + +func (q *Queries) ListMemoryNodesByBot(ctx context.Context, botID pgtype.UUID) ([]MemoryNode, error) { + rows, err := q.db.Query(ctx, listMemoryNodesByBot, botID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBotLayer = `-- name: ListMemoryNodesByBotLayer :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = $1 AND layer = $2 +ORDER BY captured_at ASC +` + +type ListMemoryNodesByBotLayerParams struct { + BotID pgtype.UUID `json:"bot_id"` + Layer string `json:"layer"` +} + +func (q *Queries) ListMemoryNodesByBotLayer(ctx context.Context, arg ListMemoryNodesByBotLayerParams) ([]MemoryNode, error) { + rows, err := q.db.Query(ctx, listMemoryNodesByBotLayer, arg.BotID, arg.Layer) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBotProfile = `-- name: ListMemoryNodesByBotProfile :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = $1 AND profile_ref = $2 +ORDER BY captured_at ASC +` + +type ListMemoryNodesByBotProfileParams struct { + BotID pgtype.UUID `json:"bot_id"` + ProfileRef string `json:"profile_ref"` +} + +func (q *Queries) ListMemoryNodesByBotProfile(ctx context.Context, arg ListMemoryNodesByBotProfileParams) ([]MemoryNode, error) { + rows, err := q.db.Query(ctx, listMemoryNodesByBotProfile, arg.BotID, arg.ProfileRef) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const upsertMemoryNode = `-- name: UpsertMemoryNode :one +INSERT INTO memory_nodes ( + id, bot_id, body, hash, layer, fact_type, subject, confidence, + metadata, source_message_ids, profile_ref, topic, captured_at, expires_at +) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) +ON CONFLICT (id) DO UPDATE SET + body = EXCLUDED.body, + hash = EXCLUDED.hash, + layer = EXCLUDED.layer, + fact_type = EXCLUDED.fact_type, + subject = EXCLUDED.subject, + confidence = EXCLUDED.confidence, + metadata = EXCLUDED.metadata, + source_message_ids = EXCLUDED.source_message_ids, + profile_ref = EXCLUDED.profile_ref, + topic = EXCLUDED.topic, + expires_at = EXCLUDED.expires_at, + updated_at = now() +RETURNING id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at +` + +type UpsertMemoryNodeParams struct { + ID string `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + Body string `json:"body"` + Hash string `json:"hash"` + Layer string `json:"layer"` + FactType string `json:"fact_type"` + Subject string `json:"subject"` + Confidence float32 `json:"confidence"` + Metadata []byte `json:"metadata"` + SourceMessageIds []byte `json:"source_message_ids"` + ProfileRef string `json:"profile_ref"` + Topic string `json:"topic"` + CapturedAt pgtype.Timestamptz `json:"captured_at"` + ExpiresAt pgtype.Timestamptz `json:"expires_at"` +} + +func (q *Queries) UpsertMemoryNode(ctx context.Context, arg UpsertMemoryNodeParams) (MemoryNode, error) { + row := q.db.QueryRow(ctx, upsertMemoryNode, + arg.ID, + arg.BotID, + arg.Body, + arg.Hash, + arg.Layer, + arg.FactType, + arg.Subject, + arg.Confidence, + arg.Metadata, + arg.SourceMessageIds, + arg.ProfileRef, + arg.Topic, + arg.CapturedAt, + arg.ExpiresAt, + ) + var i MemoryNode + err := row.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ) + return i, err +} diff --git a/internal/db/postgres/sqlc/models.go b/internal/db/postgres/sqlc/models.go index ba12e89b86..aae06578ec 100644 --- a/internal/db/postgres/sqlc/models.go +++ b/internal/db/postgres/sqlc/models.go @@ -426,6 +426,36 @@ type MediaAsset struct { CreatedAt pgtype.Timestamptz `json:"created_at"` } +type MemoryEdge struct { + ID int64 `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + SrcNode string `json:"src_node"` + DstNode string `json:"dst_node"` + Rel string `json:"rel"` + Weight float32 `json:"weight"` + Metadata []byte `json:"metadata"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type MemoryNode struct { + ID string `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + Body string `json:"body"` + Hash string `json:"hash"` + Layer string `json:"layer"` + FactType string `json:"fact_type"` + Subject string `json:"subject"` + Confidence float32 `json:"confidence"` + Metadata []byte `json:"metadata"` + SourceMessageIds []byte `json:"source_message_ids"` + ProfileRef string `json:"profile_ref"` + Topic string `json:"topic"` + CapturedAt pgtype.Timestamptz `json:"captured_at"` + ExpiresAt pgtype.Timestamptz `json:"expires_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + type MemoryProvider struct { ID pgtype.UUID `json:"id"` Name string `json:"name"` diff --git a/internal/db/sqlite/sqlc/memory_wiki.sql.go b/internal/db/sqlite/sqlc/memory_wiki.sql.go new file mode 100644 index 0000000000..2b3cd2c2ac --- /dev/null +++ b/internal/db/sqlite/sqlc/memory_wiki.sql.go @@ -0,0 +1,533 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: memory_wiki.sql + +package sqlc + +import ( + "context" + "database/sql" +) + +const countMemoryEdgesByBot = `-- name: CountMemoryEdgesByBot :one +SELECT COUNT(*) FROM memory_edges +WHERE bot_id = ?1 +` + +func (q *Queries) CountMemoryEdgesByBot(ctx context.Context, botID string) (int64, error) { + row := q.db.QueryRowContext(ctx, countMemoryEdgesByBot, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countMemoryNodesByBot = `-- name: CountMemoryNodesByBot :one +SELECT COUNT(*) FROM memory_nodes +WHERE bot_id = ?1 +` + +func (q *Queries) CountMemoryNodesByBot(ctx context.Context, botID string) (int64, error) { + row := q.db.QueryRowContext(ctx, countMemoryNodesByBot, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const deleteAllMemoryEdgesByBot = `-- name: DeleteAllMemoryEdgesByBot :exec +DELETE FROM memory_edges +WHERE bot_id = ?1 +` + +func (q *Queries) DeleteAllMemoryEdgesByBot(ctx context.Context, botID string) error { + _, err := q.db.ExecContext(ctx, deleteAllMemoryEdgesByBot, botID) + return err +} + +const deleteAllMemoryNodesByBot = `-- name: DeleteAllMemoryNodesByBot :exec +DELETE FROM memory_nodes +WHERE bot_id = ?1 +` + +func (q *Queries) DeleteAllMemoryNodesByBot(ctx context.Context, botID string) error { + _, err := q.db.ExecContext(ctx, deleteAllMemoryNodesByBot, botID) + return err +} + +const deleteMemoryEdgesByRelForBot = `-- name: DeleteMemoryEdgesByRelForBot :exec +DELETE FROM memory_edges +WHERE bot_id = ?1 AND rel = ?2 +` + +type DeleteMemoryEdgesByRelForBotParams struct { + BotID string `json:"bot_id"` + Rel string `json:"rel"` +} + +func (q *Queries) DeleteMemoryEdgesByRelForBot(ctx context.Context, arg DeleteMemoryEdgesByRelForBotParams) error { + _, err := q.db.ExecContext(ctx, deleteMemoryEdgesByRelForBot, arg.BotID, arg.Rel) + return err +} + +const deleteMemoryEdgesForNode = `-- name: DeleteMemoryEdgesForNode :exec +DELETE FROM memory_edges +WHERE bot_id = ?1 AND (src_node = ?2 OR dst_node = ?2) +` + +type DeleteMemoryEdgesForNodeParams struct { + BotID string `json:"bot_id"` + NodeID string `json:"node_id"` +} + +func (q *Queries) DeleteMemoryEdgesForNode(ctx context.Context, arg DeleteMemoryEdgesForNodeParams) error { + _, err := q.db.ExecContext(ctx, deleteMemoryEdgesForNode, arg.BotID, arg.NodeID) + return err +} + +const deleteMemoryNode = `-- name: DeleteMemoryNode :exec +DELETE FROM memory_nodes +WHERE bot_id = ?1 AND id = ?2 +` + +type DeleteMemoryNodeParams struct { + BotID string `json:"bot_id"` + ID string `json:"id"` +} + +func (q *Queries) DeleteMemoryNode(ctx context.Context, arg DeleteMemoryNodeParams) error { + _, err := q.db.ExecContext(ctx, deleteMemoryNode, arg.BotID, arg.ID) + return err +} + +const getMemoryNode = `-- name: GetMemoryNode :one +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = ?1 AND id = ?2 +` + +type GetMemoryNodeParams struct { + BotID string `json:"bot_id"` + ID string `json:"id"` +} + +func (q *Queries) GetMemoryNode(ctx context.Context, arg GetMemoryNodeParams) (MemoryNode, error) { + row := q.db.QueryRowContext(ctx, getMemoryNode, arg.BotID, arg.ID) + var i MemoryNode + err := row.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ) + return i, err +} + +const insertMemoryEdge = `-- name: InsertMemoryEdge :exec +INSERT INTO memory_edges (bot_id, src_node, dst_node, rel, weight, metadata) +VALUES ( + ?1, + ?2, + ?3, + ?4, + ?5, + ?6 +) +ON CONFLICT (bot_id, src_node, dst_node, rel) DO UPDATE SET + weight = EXCLUDED.weight, + metadata = EXCLUDED.metadata +` + +type InsertMemoryEdgeParams struct { + BotID string `json:"bot_id"` + SrcNode string `json:"src_node"` + DstNode string `json:"dst_node"` + Rel string `json:"rel"` + Weight float64 `json:"weight"` + Metadata string `json:"metadata"` +} + +func (q *Queries) InsertMemoryEdge(ctx context.Context, arg InsertMemoryEdgeParams) error { + _, err := q.db.ExecContext(ctx, insertMemoryEdge, + arg.BotID, + arg.SrcNode, + arg.DstNode, + arg.Rel, + arg.Weight, + arg.Metadata, + ) + return err +} + +const listMemoryEdgesByBot = `-- name: ListMemoryEdgesByBot :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = ?1 +` + +func (q *Queries) ListMemoryEdgesByBot(ctx context.Context, botID string) ([]MemoryEdge, error) { + rows, err := q.db.QueryContext(ctx, listMemoryEdgesByBot, botID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryEdgesByRel = `-- name: ListMemoryEdgesByRel :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = ?1 AND rel = ?2 +` + +type ListMemoryEdgesByRelParams struct { + BotID string `json:"bot_id"` + Rel string `json:"rel"` +} + +func (q *Queries) ListMemoryEdgesByRel(ctx context.Context, arg ListMemoryEdgesByRelParams) ([]MemoryEdge, error) { + rows, err := q.db.QueryContext(ctx, listMemoryEdgesByRel, arg.BotID, arg.Rel) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryEdgesFromNode = `-- name: ListMemoryEdgesFromNode :many +SELECT id, bot_id, src_node, dst_node, rel, weight, metadata, created_at FROM memory_edges +WHERE bot_id = ?1 AND src_node = ?2 +ORDER BY weight DESC +` + +type ListMemoryEdgesFromNodeParams struct { + BotID string `json:"bot_id"` + SrcNode string `json:"src_node"` +} + +func (q *Queries) ListMemoryEdgesFromNode(ctx context.Context, arg ListMemoryEdgesFromNodeParams) ([]MemoryEdge, error) { + rows, err := q.db.QueryContext(ctx, listMemoryEdgesFromNode, arg.BotID, arg.SrcNode) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryEdge + for rows.Next() { + var i MemoryEdge + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.SrcNode, + &i.DstNode, + &i.Rel, + &i.Weight, + &i.Metadata, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBot = `-- name: ListMemoryNodesByBot :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = ?1 +ORDER BY captured_at ASC +` + +func (q *Queries) ListMemoryNodesByBot(ctx context.Context, botID string) ([]MemoryNode, error) { + rows, err := q.db.QueryContext(ctx, listMemoryNodesByBot, botID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBotLayer = `-- name: ListMemoryNodesByBotLayer :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = ?1 AND layer = ?2 +ORDER BY captured_at ASC +` + +type ListMemoryNodesByBotLayerParams struct { + BotID string `json:"bot_id"` + Layer string `json:"layer"` +} + +func (q *Queries) ListMemoryNodesByBotLayer(ctx context.Context, arg ListMemoryNodesByBotLayerParams) ([]MemoryNode, error) { + rows, err := q.db.QueryContext(ctx, listMemoryNodesByBotLayer, arg.BotID, arg.Layer) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemoryNodesByBotProfile = `-- name: ListMemoryNodesByBotProfile :many +SELECT id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at FROM memory_nodes +WHERE bot_id = ?1 AND profile_ref = ?2 +ORDER BY captured_at ASC +` + +type ListMemoryNodesByBotProfileParams struct { + BotID string `json:"bot_id"` + ProfileRef string `json:"profile_ref"` +} + +func (q *Queries) ListMemoryNodesByBotProfile(ctx context.Context, arg ListMemoryNodesByBotProfileParams) ([]MemoryNode, error) { + rows, err := q.db.QueryContext(ctx, listMemoryNodesByBotProfile, arg.BotID, arg.ProfileRef) + if err != nil { + return nil, err + } + defer rows.Close() + var items []MemoryNode + for rows.Next() { + var i MemoryNode + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const upsertMemoryNode = `-- name: UpsertMemoryNode :one +INSERT INTO memory_nodes ( + id, bot_id, body, hash, layer, fact_type, subject, confidence, + metadata, source_message_ids, profile_ref, topic, captured_at, expires_at +) +VALUES ( + ?1, + ?2, + ?3, + ?4, + ?5, + ?6, + ?7, + ?8, + ?9, + ?10, + ?11, + ?12, + ?13, + ?14 +) +ON CONFLICT (id) DO UPDATE SET + body = EXCLUDED.body, + hash = EXCLUDED.hash, + layer = EXCLUDED.layer, + fact_type = EXCLUDED.fact_type, + subject = EXCLUDED.subject, + confidence = EXCLUDED.confidence, + metadata = EXCLUDED.metadata, + source_message_ids = EXCLUDED.source_message_ids, + profile_ref = EXCLUDED.profile_ref, + topic = EXCLUDED.topic, + expires_at = EXCLUDED.expires_at, + updated_at = CURRENT_TIMESTAMP +RETURNING id, bot_id, body, hash, layer, fact_type, subject, confidence, metadata, source_message_ids, profile_ref, topic, captured_at, expires_at, updated_at, created_at +` + +type UpsertMemoryNodeParams struct { + ID string `json:"id"` + BotID string `json:"bot_id"` + Body string `json:"body"` + Hash string `json:"hash"` + Layer string `json:"layer"` + FactType string `json:"fact_type"` + Subject string `json:"subject"` + Confidence float64 `json:"confidence"` + Metadata string `json:"metadata"` + SourceMessageIds string `json:"source_message_ids"` + ProfileRef string `json:"profile_ref"` + Topic string `json:"topic"` + CapturedAt string `json:"captured_at"` + ExpiresAt sql.NullString `json:"expires_at"` +} + +func (q *Queries) UpsertMemoryNode(ctx context.Context, arg UpsertMemoryNodeParams) (MemoryNode, error) { + row := q.db.QueryRowContext(ctx, upsertMemoryNode, + arg.ID, + arg.BotID, + arg.Body, + arg.Hash, + arg.Layer, + arg.FactType, + arg.Subject, + arg.Confidence, + arg.Metadata, + arg.SourceMessageIds, + arg.ProfileRef, + arg.Topic, + arg.CapturedAt, + arg.ExpiresAt, + ) + var i MemoryNode + err := row.Scan( + &i.ID, + &i.BotID, + &i.Body, + &i.Hash, + &i.Layer, + &i.FactType, + &i.Subject, + &i.Confidence, + &i.Metadata, + &i.SourceMessageIds, + &i.ProfileRef, + &i.Topic, + &i.CapturedAt, + &i.ExpiresAt, + &i.UpdatedAt, + &i.CreatedAt, + ) + return i, err +} diff --git a/internal/db/sqlite/sqlc/models.go b/internal/db/sqlite/sqlc/models.go index bec737c17c..18c7773932 100644 --- a/internal/db/sqlite/sqlc/models.go +++ b/internal/db/sqlite/sqlc/models.go @@ -410,6 +410,36 @@ type McpOauthToken struct { UpdatedAt string `json:"updated_at"` } +type MemoryEdge struct { + ID int64 `json:"id"` + BotID string `json:"bot_id"` + SrcNode string `json:"src_node"` + DstNode string `json:"dst_node"` + Rel string `json:"rel"` + Weight float64 `json:"weight"` + Metadata string `json:"metadata"` + CreatedAt string `json:"created_at"` +} + +type MemoryNode struct { + ID string `json:"id"` + BotID string `json:"bot_id"` + Body string `json:"body"` + Hash string `json:"hash"` + Layer string `json:"layer"` + FactType string `json:"fact_type"` + Subject string `json:"subject"` + Confidence float64 `json:"confidence"` + Metadata string `json:"metadata"` + SourceMessageIds string `json:"source_message_ids"` + ProfileRef string `json:"profile_ref"` + Topic string `json:"topic"` + CapturedAt string `json:"captured_at"` + ExpiresAt sql.NullString `json:"expires_at"` + UpdatedAt string `json:"updated_at"` + CreatedAt string `json:"created_at"` +} + type MemoryProvider struct { ID string `json:"id"` Name string `json:"name"` diff --git a/internal/memory/migrate/tomemorywiki.go b/internal/memory/migrate/tomemorywiki.go new file mode 100644 index 0000000000..ff407870a8 --- /dev/null +++ b/internal/memory/migrate/tomemorywiki.go @@ -0,0 +1,309 @@ +// Package migrate contains utilities that convert existing markdown-backed +// memory content into the PostgreSQL/SQLite wiki/graph schema +// (memory_nodes + memory_edges). The conversion logic is backend-agnostic: it +// produces plain NodeSpec/EdgeSpec values so it can be exercised by unit tests +// without a live database, and persisted by any concrete store implementation. +package migrate + +import ( + "fmt" + "sort" + "strings" + "time" + + storefs "github.com/memohai/memoh/internal/memory/storefs" +) + +// MemoryLayer is the canonical layer a memory node belongs to. Existing flat +// markdown entries that carry no layer hint are classified as LayerNote. +type MemoryLayer string + +const ( + LayerPreference MemoryLayer = "preference" + LayerIdentity MemoryLayer = "identity" + LayerContext MemoryLayer = "context" + LayerExperience MemoryLayer = "experience" + LayerActivity MemoryLayer = "activity" + LayerPersona MemoryLayer = "persona" + LayerNote MemoryLayer = "note" +) + +// EdgeRel is the canonical relationship type between two memory nodes. +type EdgeRel string + +const ( + EdgeSameProfile EdgeRel = "same_profile" + EdgeSameTopic EdgeRel = "same_topic" + EdgeSameDay EdgeRel = "same_day" +) + +// NodeSpec is a backend-agnostic description of a memory_nodes row, produced +// from a storefs.MemoryItem. Concrete stores (PG/SQLite) translate this into +// their sqlc UpsertMemoryNodeParams. +type NodeSpec struct { + ID string + BotID string + Body string + Hash string + Layer MemoryLayer + FactType string + Subject string + Confidence float32 + Metadata map[string]any + SourceMessageIDs []string + ProfileRef string + Topic string + CapturedAt time.Time + ExpiresAt time.Time // zero value means "no expiry". +} + +// EdgeSpec is a backend-agnostic description of a memory_edges row. +type EdgeSpec struct { + BotID string + SrcNode string + DstNode string + Rel EdgeRel + Weight float32 + Metadata map[string]any +} + +// Result summarises a wiki backfill pass for one bot. +type Result struct { + BotID string + NodeCount int + EdgeCount int + LayerBreak map[MemoryLayer]int +} + +// Plan converts a bot's markdown memory items into wiki node specs plus the +// implicit edges derivable from shared profile_ref / topic / captured day. +// It performs no I/O and is safe to call in dry-run mode. +// +// Layer classification is intentionally conservative: items without any hint +// fall back to LayerNote. This keeps the backfill deterministic and reversible +// until the typed-facts formation (P1) starts emitting explicit layers. +func Plan(botID string, items []storefs.MemoryItem) ([]NodeSpec, []EdgeSpec) { + nodes := make([]NodeSpec, 0, len(items)) + for _, item := range items { + nodes = append(nodes, nodeFromItem(botID, item)) + } + edges := buildImplicitEdges(nodes) + return nodes, edges +} + +// Summarise returns a Result for a planned node/edge set, suitable for CLI +// dry-run reporting. +func Summarise(botID string, nodes []NodeSpec, edges []EdgeSpec) Result { + r := Result{BotID: botID, NodeCount: len(nodes), EdgeCount: len(edges), LayerBreak: map[MemoryLayer]int{}} + for _, n := range nodes { + layer := n.Layer + if layer == "" { + layer = LayerNote + } + r.LayerBreak[layer]++ + } + return r +} + +func nodeFromItem(botID string, item storefs.MemoryItem) NodeSpec { + body := strings.TrimSpace(item.Memory) + layer := classifyLayer(item) + topic := metadataString(item.Metadata, "topic") + profileRef := metadataString(item.Metadata, "profile_ref") + if profileRef == "" { + profileRef = metadataString(item.Metadata, "profile_user_id") + } + captured := parseTime(item.CreatedAt) + if captured.IsZero() { + captured = parseTime(item.UpdatedAt) + } + if captured.IsZero() { + captured = time.Now().UTC() + } + return NodeSpec{ + ID: strings.TrimSpace(item.ID), + BotID: botID, + Body: body, + Hash: strings.TrimSpace(item.Hash), + Layer: layer, + Subject: metadataString(item.Metadata, "subject"), + Confidence: metadataFloat(item.Metadata, "confidence", 0.5), + Metadata: cloneMetadata(item.Metadata), + ProfileRef: profileRef, + Topic: topic, + CapturedAt: captured, + } +} + +// classifyLayer maps a flat memory item to a canonical layer using light +// metadata heuristics. Items that already declare a `layer` metadata key are +// honoured (validated against the known set); otherwise the item defaults to +// LayerNote. This is deliberately non-magical: real classification happens +// later in the typed-facts formation (P1). +func classifyLayer(item storefs.MemoryItem) MemoryLayer { + if raw := metadataString(item.Metadata, "layer"); raw != "" { + switch MemoryLayer(strings.ToLower(strings.TrimSpace(raw))) { + case LayerPreference, LayerIdentity, LayerContext, LayerExperience, LayerActivity, LayerPersona, LayerNote: + return MemoryLayer(raw) + } + } + return LayerNote +} + +// buildImplicitEdges derives same_profile / same_topic / same_day edges between +// nodes. Edges are undirected in intent but stored as directed src->dst pairs +// where src < dst (lexicographically by node ID) to avoid duplicates. A node +// never edges to itself. +func buildImplicitEdges(nodes []NodeSpec) []EdgeSpec { + if len(nodes) < 2 { + return nil + } + byProfile := indexBy(nodes, func(n NodeSpec) string { return n.ProfileRef }) + byTopic := indexBy(nodes, func(n NodeSpec) string { return n.Topic }) + byDay := indexBy(nodes, func(n NodeSpec) string { return n.CapturedAt.UTC().Format("2006-01-02") }) + + seen := map[string]struct{}{} + edges := make([]EdgeSpec, 0) + add := func(a, b NodeSpec, rel EdgeRel, weight float32) { + if a.ID == b.ID { + return + } + src, dst := a.ID, b.ID + if dst < src { + src, dst = dst, src + } + key := src + "\x00" + dst + "\x00" + string(rel) + if _, ok := seen[key]; ok { + return + } + seen[key] = struct{}{} + edges = append(edges, EdgeSpec{BotID: a.BotID, SrcNode: src, DstNode: dst, Rel: rel, Weight: weight}) + } + + emit := func(groups [][]NodeSpec, rel EdgeRel, weight float32) { + for _, group := range groups { + if len(group) < 2 { + continue + } + for i := 0; i < len(group); i++ { + for j := i + 1; j < len(group); j++ { + add(group[i], group[j], rel, weight) + } + } + } + } + emit(byProfile, EdgeSameProfile, 1.0) + emit(byTopic, EdgeSameTopic, 0.8) + emit(byDay, EdgeSameDay, 0.5) + + // Deterministic ordering keeps dry-run output stable. + sort.Slice(edges, func(i, j int) bool { + if edges[i].Rel != edges[j].Rel { + return edges[i].Rel < edges[j].Rel + } + if edges[i].SrcNode != edges[j].SrcNode { + return edges[i].SrcNode < edges[j].SrcNode + } + return edges[i].DstNode < edges[j].DstNode + }) + return edges +} + +// indexBy groups nodes sharing the same non-empty key. +func indexBy(nodes []NodeSpec, key func(NodeSpec) string) [][]NodeSpec { + buckets := map[string][]NodeSpec{} + order := []string{} + for _, n := range nodes { + k := strings.TrimSpace(key(n)) + if k == "" { + continue + } + if _, ok := buckets[k]; !ok { + order = append(order, k) + } + buckets[k] = append(buckets[k], n) + } + out := make([][]NodeSpec, 0, len(buckets)) + for _, k := range order { + out = append(out, buckets[k]) + } + return out +} + +func metadataString(m map[string]any, key string) string { + if m == nil { + return "" + } + v, ok := m[key] + if !ok || v == nil { + return "" + } + switch s := v.(type) { + case string: + return strings.TrimSpace(s) + default: + return strings.TrimSpace(toString(v)) + } +} + +func metadataFloat(m map[string]any, key string, def float32) float32 { + if m == nil { + return def + } + v, ok := m[key] + if !ok || v == nil { + return def + } + switch n := v.(type) { + case float64: + return clamp32(float32(n), def) + case float32: + return clamp32(n, def) + case int: + return clamp32(float32(n), def) + case int64: + return clamp32(float32(n), def) + case string: + return def + default: + return def + } +} + +func clamp32(v, def float32) float32 { + if v < 0 || v > 1 { + return def + } + return v +} + +func cloneMetadata(m map[string]any) map[string]any { + if len(m) == 0 { + return nil + } + out := make(map[string]any, len(m)) + for k, v := range m { + out[k] = v + } + return out +} + +func parseTime(s string) time.Time { + s = strings.TrimSpace(s) + if s == "" { + return time.Time{} + } + for _, layout := range []string{time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05Z", "2006-01-02 15:04:05", "2006-01-02"} { + if t, err := time.Parse(layout, s); err == nil { + return t.UTC() + } + } + return time.Time{} +} + +func toString(v any) string { + if v == nil { + return "" + } + return strings.TrimSpace(fmt.Sprint(v)) +} diff --git a/internal/memory/migrate/tomemorywiki_test.go b/internal/memory/migrate/tomemorywiki_test.go new file mode 100644 index 0000000000..03e537e96e --- /dev/null +++ b/internal/memory/migrate/tomemorywiki_test.go @@ -0,0 +1,186 @@ +package migrate + +import ( + "testing" + "time" + + storefs "github.com/memohai/memoh/internal/memory/storefs" +) + +func TestPlanClassifiesLayersAndEdges(t *testing.T) { + items := []storefs.MemoryItem{ + { + ID: "bot-1:mem_1", + Memory: "User prefers oolong tea", + Hash: "h1", + CreatedAt: "2026-06-01T10:00:00Z", + Metadata: map[string]any{"layer": "preference", "profile_ref": "user:42", "topic": "drinks", "confidence": 0.9}, + }, + { + ID: "bot-1:mem_2", + Memory: "User lives in Berlin", + Hash: "h2", + CreatedAt: "2026-06-01T12:00:00Z", + Metadata: map[string]any{"profile_ref": "user:42", "topic": "location"}, + }, + { + ID: "bot-1:mem_3", + Memory: "Met about the API project", + Hash: "h3", + CreatedAt: "2026-06-02T09:00:00Z", + Metadata: map[string]any{"topic": "work"}, + }, + { + ID: "bot-1:mem_4", + Memory: "Unrelated note", + Hash: "h4", + CreatedAt: "2026-06-03T09:00:00Z", + }, + } + + nodes, edges := Plan("bot-1", items) + + if len(nodes) != 4 { + t.Fatalf("expected 4 nodes, got %d", len(nodes)) + } + + // Layer classification: explicit layer honoured, otherwise note. + wantLayer := map[string]MemoryLayer{ + "bot-1:mem_1": LayerPreference, + "bot-1:mem_2": LayerNote, + "bot-1:mem_3": LayerNote, + "bot-1:mem_4": LayerNote, + } + for _, n := range nodes { + if got, want := n.Layer, wantLayer[n.ID]; got != want { + t.Fatalf("node %s layer = %q, want %q", n.ID, got, want) + } + } + + // Confidence: explicit 0.9 preserved, others default to 0.5. + conf := map[string]float32{} + for _, n := range nodes { + conf[n.ID] = n.Confidence + } + if conf["bot-1:mem_1"] != 0.9 { + t.Fatalf("mem_1 confidence = %v, want 0.9", conf["bot-1:mem_1"]) + } + if conf["bot-1:mem_2"] != 0.5 { + t.Fatalf("mem_2 confidence = %v, want 0.5 (default)", conf["bot-1:mem_2"]) + } + + // Profile edge: mem_1 <-> mem_2 share user:42. + // Day edge: mem_1 <-> mem_2 share 2026-06-01. + // No topic edges (all distinct topics). + got := edgeSet(edges) + expectEdge(t, got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameProfile) + expectEdge(t, got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameDay) + if _, ok := lookupEdge(got, "bot-1:mem_1", "bot-1:mem_2", EdgeSameTopic); ok { + t.Fatal("did not expect a same_topic edge between mem_1 and mem_2 (different topics)") + } + // mem_3 and mem_4 have no shared key with anything -> no edges. + for _, rel := range []EdgeRel{EdgeSameProfile, EdgeSameTopic, EdgeSameDay} { + for _, other := range []string{"bot-1:mem_1", "bot-1:mem_2", "bot-1:mem_4"} { + if _, ok := lookupEdge(got, "bot-1:mem_3", other, rel); ok { + t.Fatalf("did not expect edge mem_3 <-> %s (%s)", other, rel) + } + } + } +} + +func TestPlanRejectsInvalidLayerAndConfidence(t *testing.T) { + items := []storefs.MemoryItem{ + { + ID: "bot-1:bad-layer", + Memory: "x", + CreatedAt: "2026-06-01T00:00:00Z", + // Unknown layer value falls back to note. + Metadata: map[string]any{"layer": "nonsense"}, + }, + { + ID: "bot-1:bad-conf", + Memory: "y", + CreatedAt: "2026-06-01T00:00:00Z", + // Out-of-range confidence falls back to default. + Metadata: map[string]any{"confidence": 2.5}, + }, + } + nodes, _ := Plan("bot-1", items) + byID := map[string]NodeSpec{} + for _, n := range nodes { + byID[n.ID] = n + } + if byID["bot-1:bad-layer"].Layer != LayerNote { + t.Fatalf("invalid layer should fall back to note, got %q", byID["bot-1:bad-layer"].Layer) + } + if byID["bot-1:bad-conf"].Confidence != 0.5 { + t.Fatalf("out-of-range confidence should fall back to 0.5, got %v", byID["bot-1:bad-conf"].Confidence) + } +} + +func TestPlanEmptyAndSingleton(t *testing.T) { + if nodes, edges := Plan("bot-1", nil); len(nodes) != 0 || len(edges) != 0 { + t.Fatalf("empty input should yield empty plan, got %d nodes %d edges", len(nodes), len(edges)) + } + nodes, edges := Plan("bot-1", []storefs.MemoryItem{{ID: "bot-1:mem_1", Memory: "x", CreatedAt: "2026-06-01T00:00:00Z"}}) + if len(nodes) != 1 || len(edges) != 0 { + t.Fatalf("singleton should yield 1 node 0 edges, got %d nodes %d edges", len(nodes), len(edges)) + } +} + +func TestPlanFallsBackToUpdatedAtThenNow(t *testing.T) { + noCreated := storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "x", UpdatedAt: "2026-05-01T00:00:00Z"} + nodes, _ := Plan("bot-1", []storefs.MemoryItem{noCreated}) + if !nodes[0].CapturedAt.Equal(time.Date(2026, 5, 1, 0, 0, 0, 0, time.UTC)) { + t.Fatalf("captured_at should fall back to updated_at, got %v", nodes[0].CapturedAt) + } + + neither := storefs.MemoryItem{ID: "bot-1:mem_1", Memory: "x"} + nodes2, _ := Plan("bot-1", []storefs.MemoryItem{neither}) + if nodes2[0].CapturedAt.IsZero() { + t.Fatal("captured_at should fall back to now when both created_at and updated_at are absent") + } +} + +func TestSummariseCountsLayers(t *testing.T) { + items := []storefs.MemoryItem{ + {ID: "bot-1:mem_1", Memory: "a", CreatedAt: "2026-06-01T00:00:00Z", Metadata: map[string]any{"layer": "preference"}}, + {ID: "bot-1:mem_2", Memory: "b", CreatedAt: "2026-06-02T00:00:00Z", Metadata: map[string]any{"layer": "preference"}}, + {ID: "bot-1:mem_3", Memory: "c", CreatedAt: "2026-06-03T00:00:00Z"}, + } + nodes, edges := Plan("bot-1", items) + r := Summarise("bot-1", nodes, edges) + if r.NodeCount != 3 { + t.Fatalf("node count = %d, want 3", r.NodeCount) + } + if r.LayerBreak[LayerPreference] != 2 { + t.Fatalf("preference count = %d, want 2", r.LayerBreak[LayerPreference]) + } + if r.LayerBreak[LayerNote] != 1 { + t.Fatalf("note count = %d, want 1", r.LayerBreak[LayerNote]) + } +} + +func edgeSet(edges []EdgeSpec) map[string]struct{} { + out := map[string]struct{}{} + for _, e := range edges { + out[e.SrcNode+"\x00"+e.DstNode+"\x00"+string(e.Rel)] = struct{}{} + } + return out +} + +func lookupEdge(set map[string]struct{}, a, b string, rel EdgeRel) (struct{}, bool) { + src, dst := a, b + if dst < src { + src, dst = dst, src + } + _, ok := set[src+"\x00"+dst+"\x00"+string(rel)] + return struct{}{}, ok +} + +func expectEdge(t *testing.T, set map[string]struct{}, a, b string, rel EdgeRel) { + t.Helper() + if _, ok := lookupEdge(set, a, b, rel); !ok { + t.Fatalf("expected edge %s <-> %s (%s) not found", a, b, rel) + } +} From e3f8ce81e4a149050d515b2785a66470609d9303 Mon Sep 17 00:00:00 2001 From: Ran <16112591+chen-ran@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:41:08 +0800 Subject: [PATCH 3/3] chore(spec): regenerate OpenAPI spec and TS SDK Drop the sparse-explain TopKBucket/CDFPoint types and the unused EmbedInput/EmbedUpsertRequest/EmbedUpsertResponse adapters types from the generated OpenAPI schema and the @memohai/sdk TypeScript client. --- packages/sdk/src/index.ts | 2 +- packages/sdk/src/types.gen.ts | 25 --------------------- spec/docs.go | 41 ----------------------------------- spec/swagger.json | 41 ----------------------------------- spec/swagger.yaml | 28 ------------------------ 5 files changed, 1 insertion(+), 136 deletions(-) diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 3816d89c4f..cee1f6d90c 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts export { deleteBotsByBotIdAclRulesByRuleId, deleteBotsByBotIdAcpRuntimesByRuntimeId, deleteBotsByBotIdChannelManagersByChannelIdentityId, deleteBotsByBotIdCompactionLogs, deleteBotsByBotIdContainer, deleteBotsByBotIdContainerBrowserSessionsBySessionId, deleteBotsByBotIdContainerDisplaySessionsBySessionId, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdEmailBindingsById, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdMcpById, deleteBotsByBotIdMcpByIdOauthToken, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdPluginsById, deleteBotsByBotIdScheduleById, deleteBotsByBotIdScheduleLogs, deleteBotsByBotIdSessionsBySessionId, deleteBotsByBotIdSettings, deleteBotsByBotIdUserAccessByGrantId, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteEmailProvidersById, deleteEmailProvidersByIdOauthToken, deleteFetchProvidersById, deleteMemoryProvidersById, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteProvidersByIdOauthToken, deleteSearchProvidersById, deleteUsersById, deleteUsersMeChannelIdentitiesByChannelIdentityId, getAcpProfiles, getBots, getBotsByBotIdAclChannelIdentities, getBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversations, getBotsByBotIdAclChannelTypesByChannelTypeConversations, getBotsByBotIdAclDefaultEffect, getBotsByBotIdAclRules, getBotsByBotIdAcpClaudeCodeOauthAuthorize, getBotsByBotIdAcpClaudeCodeOauthStatus, getBotsByBotIdAcpRuntimesByRuntimeId, getBotsByBotIdBackupSummary, getBotsByBotIdChannelManagers, getBotsByBotIdCompactionLogs, getBotsByBotIdContainer, getBotsByBotIdContainerDisplay, getBotsByBotIdContainerDisplaySessions, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerMetrics, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdContainerTerminal, getBotsByBotIdContainerTerminalWs, getBotsByBotIdEmailBindings, getBotsByBotIdEmailOutbox, getBotsByBotIdEmailOutboxById, getBotsByBotIdHeartbeatLogs, getBotsByBotIdHooksEvents, getBotsByBotIdLocalStream, getBotsByBotIdLocalWs, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpByIdOauthStatus, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryStatus, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdMessagesLocate, getBotsByBotIdPlugins, getBotsByBotIdPluginsById, getBotsByBotIdPluginsByIdOauthStatus, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdScheduleByIdLogs, getBotsByBotIdScheduleLogs, getBotsByBotIdSessions, getBotsByBotIdSessionsBySessionId, getBotsByBotIdSessionsBySessionIdAcpRuntime, getBotsByBotIdSessionsBySessionIdMessagesEvents, getBotsByBotIdSessionsBySessionIdStatus, getBotsByBotIdSessionsEvents, getBotsByBotIdSettings, getBotsByBotIdTokenUsage, getBotsByBotIdTokenUsageRecords, getBotsByBotIdUserAccess, getBotsByBotIdUserAccessCandidates, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsNameAvailability, getChannels, getChannelsByPlatform, getEmailOauthCallback, getEmailProviders, getEmailProvidersById, getEmailProvidersByIdOauthAuthorize, getEmailProvidersByIdOauthStatus, getEmailProvidersMeta, getFetchProviders, getFetchProvidersById, getFetchProvidersMeta, getMemoryProviders, getMemoryProvidersById, getMemoryProvidersByIdStatus, getMemoryProvidersMeta, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getOauthMcpCallback, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersByIdOauthAuthorize, getProvidersByIdOauthStatus, getProvidersCount, getProvidersNameByName, getProvidersOauthCallback, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getSpeechModels, getSpeechModelsById, getSpeechModelsByIdCapabilities, getSpeechProviders, getSpeechProvidersById, getSpeechProvidersByIdModels, getSpeechProvidersMeta, getSupermarketPlugins, getSupermarketPluginsById, getSupermarketSkills, getSupermarketSkillsById, getSupermarketTags, getTranscriptionModels, getTranscriptionModelsById, getTranscriptionModelsByIdCapabilities, getTranscriptionProviders, getTranscriptionProvidersById, getTranscriptionProvidersByIdModels, getTranscriptionProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelIdentities, getUsersMeChannelsByPlatform, type Options, patchBotsByBotIdAcpRuntimesByRuntimeIdModel, patchBotsByBotIdSessionsBySessionId, patchBotsByBotIdSessionsBySessionIdAcpRuntimeModel, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsBackupImport, postBotsBackupImportPreview, postBotsByBotIdAclRules, postBotsByBotIdAcpClaudeCodeOauthExchange, postBotsByBotIdAcpRuntimes, postBotsByBotIdBackupExport, postBotsByBotIdChannelManagers, postBotsByBotIdContainer, postBotsByBotIdContainerBrowserSessions, postBotsByBotIdContainerBrowserSessionsBySessionIdKeepalive, postBotsByBotIdContainerDataRestore, postBotsByBotIdContainerDisplayPrepare, postBotsByBotIdContainerDisplayWebrtcOffer, postBotsByBotIdContainerFsArchive, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsExtract, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSkillsActions, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerSnapshotsRollback, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdEmailBindings, postBotsByBotIdHooksTest, postBotsByBotIdLocalMessages, postBotsByBotIdMcp, postBotsByBotIdMcpByIdOauthAuthorize, postBotsByBotIdMcpByIdOauthDiscover, postBotsByBotIdMcpByIdOauthExchange, postBotsByBotIdMcpByIdProbe, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdPluginsByIdDisable, postBotsByBotIdPluginsByIdEnable, postBotsByBotIdPluginsByIdOauthAuthorize, postBotsByBotIdPluginsByIdUninstall, postBotsByBotIdSchedule, postBotsByBotIdSessions, postBotsByBotIdSessionsBySessionIdAcpRuntime, postBotsByBotIdSessionsBySessionIdCompact, postBotsByBotIdSettings, postBotsByBotIdSupermarketInstallPlugin, postBotsByBotIdSupermarketInstallSkill, postBotsByBotIdToolApprovalsByApprovalIdApprove, postBotsByBotIdToolApprovalsByApprovalIdReject, postBotsByBotIdTools, postBotsByBotIdTtsSynthesize, postBotsByBotIdUserAccess, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmailMailgunWebhookByConfigId, postEmailProviders, postFetchProviders, postMemoryProviders, postModels, postModelsByIdTest, postProviders, postProvidersByIdImportModels, postProvidersByIdOauthPoll, postProvidersByIdTest, postSearchProviders, postSpeechModelsByIdTest, postSpeechProvidersByIdImportModels, postTranscriptionModelsByIdTest, postTranscriptionProvidersByIdImportModels, postUsers, postUsersMeChannelLinks, putBotsByBotIdAclDefaultEffect, putBotsByBotIdAclRulesByRuleId, putBotsByBotIdContainerMetrics, putBotsByBotIdEmailBindingsById, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdUserAccessByGrantId, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdOwner, putEmailProvidersById, putFetchProvidersById, putMemoryProvidersById, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putSpeechModelsById, putTranscriptionModelsById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen'; -export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileMetadata, AccountsUpdateProfileRequest, AclChannelIdentityCandidate, AclChannelIdentityCandidateListResponse, AclCreateRuleRequest, AclDefaultEffectResponse, AclListRulesResponse, AclObservedConversationCandidate, AclObservedConversationCandidateListResponse, AclRule, AclSourceScope, AclUpdateRuleRequest, AcpagentRuntimeStatus, AcpclientModelInfo, AcpclientModelState, AcpprofileManagedField, AcpprofileProfilesResponse, AcpprofilePublicProfile, AdaptersCdfPoint, AdaptersCompactResult, AdaptersDeleteResponse, AdaptersHealthStatus, AdaptersMemoryCompactCapability, AdaptersMemoryItem, AdaptersMemoryStatusResponse, AdaptersMessage, AdaptersProviderCollectionStatus, AdaptersProviderConfigSchema, AdaptersProviderCreateRequest, AdaptersProviderFieldSchema, AdaptersProviderGetResponse, AdaptersProviderMeta, AdaptersProviderStatusResponse, AdaptersProviderType, AdaptersProviderUpdateRequest, AdaptersRebuildResult, AdaptersSearchResponse, AdaptersTopKBucket, AdaptersUsageResponse, AudioConfigSchema, AudioFieldSchema, AudioImportModelsResponse, AudioModelCapabilities, AudioModelInfo, AudioParamConstraint, AudioProviderMetaResponse, AudioSpeechModelResponse, AudioSpeechProviderResponse, AudioTestSynthesizeRequest, AudioTestTranscriptionResponse, AudioTranscriptionModelResponse, AudioTranscriptionWord, AudioUpdateSpeechModelRequest, AudioVoiceInfo, BotbackupExportRequest, BotbackupImportMode, BotbackupImportResult, BotbackupManifest, BotbackupManifestEntry, BotbackupManifestOptions, BotbackupPreviewResult, BotbackupProfilePreview, BotbackupRestorePlan, BotbackupSection, BotbackupSectionSummary, BotbackupSummaryResult, BotsBot, BotsBotCheck, BotsCreateBotRequest, BotsCreateUserGrantRequest, BotsListBotsResponse, BotsListChecksResponse, BotsNameAvailability, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpdateUserGrantRequest, BotsUserGrant, ChannelaccessBinding, ChannelaccessIssueLinkCodeRequest, ChannelaccessLinkCode, ChannelaccessListBindingsResponse, ChannelaccessListManagersResponse, ChannelaccessManager, ChannelaccessSetManagerRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelChannelType, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelForwardRef, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, CompactionListLogsResponse, CompactionLog, DeleteBotsByBotIdAclRulesByRuleIdData, DeleteBotsByBotIdAclRulesByRuleIdError, DeleteBotsByBotIdAclRulesByRuleIdErrors, DeleteBotsByBotIdAclRulesByRuleIdResponses, DeleteBotsByBotIdAcpRuntimesByRuntimeIdData, DeleteBotsByBotIdAcpRuntimesByRuntimeIdError, DeleteBotsByBotIdAcpRuntimesByRuntimeIdErrors, DeleteBotsByBotIdAcpRuntimesByRuntimeIdResponses, DeleteBotsByBotIdChannelManagersByChannelIdentityIdData, DeleteBotsByBotIdChannelManagersByChannelIdentityIdError, DeleteBotsByBotIdChannelManagersByChannelIdentityIdErrors, DeleteBotsByBotIdChannelManagersByChannelIdentityIdResponses, DeleteBotsByBotIdCompactionLogsData, DeleteBotsByBotIdCompactionLogsError, DeleteBotsByBotIdCompactionLogsErrors, DeleteBotsByBotIdCompactionLogsResponses, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdData, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdError, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdErrors, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdResponses, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdError, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdErrors, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdResponses, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdOauthTokenData, DeleteBotsByBotIdMcpByIdOauthTokenError, DeleteBotsByBotIdMcpByIdOauthTokenErrors, DeleteBotsByBotIdMcpByIdOauthTokenResponses, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdPluginsByIdData, DeleteBotsByBotIdPluginsByIdError, DeleteBotsByBotIdPluginsByIdErrors, DeleteBotsByBotIdPluginsByIdResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdScheduleLogsData, DeleteBotsByBotIdScheduleLogsError, DeleteBotsByBotIdScheduleLogsErrors, DeleteBotsByBotIdScheduleLogsResponses, DeleteBotsByBotIdSessionsBySessionIdData, DeleteBotsByBotIdSessionsBySessionIdError, DeleteBotsByBotIdSessionsBySessionIdErrors, DeleteBotsByBotIdSessionsBySessionIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdUserAccessByGrantIdData, DeleteBotsByBotIdUserAccessByGrantIdError, DeleteBotsByBotIdUserAccessByGrantIdErrors, DeleteBotsByBotIdUserAccessByGrantIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdOauthTokenData, DeleteEmailProvidersByIdOauthTokenError, DeleteEmailProvidersByIdOauthTokenErrors, DeleteEmailProvidersByIdOauthTokenResponses, DeleteEmailProvidersByIdResponses, DeleteFetchProvidersByIdData, DeleteFetchProvidersByIdError, DeleteFetchProvidersByIdErrors, DeleteFetchProvidersByIdResponses, DeleteMemoryProvidersByIdData, DeleteMemoryProvidersByIdError, DeleteMemoryProvidersByIdErrors, DeleteMemoryProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdOauthTokenData, DeleteProvidersByIdOauthTokenError, DeleteProvidersByIdOauthTokenErrors, DeleteProvidersByIdOauthTokenResponses, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, DeleteUsersByIdData, DeleteUsersByIdError, DeleteUsersByIdErrors, DeleteUsersByIdResponses, DeleteUsersMeChannelIdentitiesByChannelIdentityIdData, DeleteUsersMeChannelIdentitiesByChannelIdentityIdError, DeleteUsersMeChannelIdentitiesByChannelIdentityIdErrors, DeleteUsersMeChannelIdentitiesByChannelIdentityIdResponses, DisplaySessionInfo, EmailBindingResponse, EmailConfigSchema, EmailCreateBindingRequest, EmailCreateProviderRequest, EmailFieldSchema, EmailOutboxItemResponse, EmailProviderMeta, EmailProviderResponse, EmailUpdateBindingRequest, EmailUpdateProviderRequest, FetchprovidersCreateRequest, FetchprovidersGetResponse, FetchprovidersProviderConfigSchema, FetchprovidersProviderFieldSchema, FetchprovidersProviderMeta, FetchprovidersProviderName, FetchprovidersUpdateRequest, GetAcpProfilesData, GetAcpProfilesResponse, GetAcpProfilesResponses, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsData, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsError, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsErrors, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponse, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponses, GetBotsByBotIdAclChannelIdentitiesData, GetBotsByBotIdAclChannelIdentitiesError, GetBotsByBotIdAclChannelIdentitiesErrors, GetBotsByBotIdAclChannelIdentitiesResponse, GetBotsByBotIdAclChannelIdentitiesResponses, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsData, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsError, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsErrors, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponse, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponses, GetBotsByBotIdAclDefaultEffectData, GetBotsByBotIdAclDefaultEffectError, GetBotsByBotIdAclDefaultEffectErrors, GetBotsByBotIdAclDefaultEffectResponse, GetBotsByBotIdAclDefaultEffectResponses, GetBotsByBotIdAclRulesData, GetBotsByBotIdAclRulesError, GetBotsByBotIdAclRulesErrors, GetBotsByBotIdAclRulesResponse, GetBotsByBotIdAclRulesResponses, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeData, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeError, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeErrors, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponse, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponses, GetBotsByBotIdAcpClaudeCodeOauthStatusData, GetBotsByBotIdAcpClaudeCodeOauthStatusError, GetBotsByBotIdAcpClaudeCodeOauthStatusErrors, GetBotsByBotIdAcpClaudeCodeOauthStatusResponse, GetBotsByBotIdAcpClaudeCodeOauthStatusResponses, GetBotsByBotIdAcpRuntimesByRuntimeIdData, GetBotsByBotIdAcpRuntimesByRuntimeIdError, GetBotsByBotIdAcpRuntimesByRuntimeIdErrors, GetBotsByBotIdAcpRuntimesByRuntimeIdResponse, GetBotsByBotIdAcpRuntimesByRuntimeIdResponses, GetBotsByBotIdBackupSummaryData, GetBotsByBotIdBackupSummaryError, GetBotsByBotIdBackupSummaryErrors, GetBotsByBotIdBackupSummaryResponse, GetBotsByBotIdBackupSummaryResponses, GetBotsByBotIdChannelManagersData, GetBotsByBotIdChannelManagersError, GetBotsByBotIdChannelManagersErrors, GetBotsByBotIdChannelManagersResponse, GetBotsByBotIdChannelManagersResponses, GetBotsByBotIdCompactionLogsData, GetBotsByBotIdCompactionLogsError, GetBotsByBotIdCompactionLogsErrors, GetBotsByBotIdCompactionLogsResponse, GetBotsByBotIdCompactionLogsResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerDisplayData, GetBotsByBotIdContainerDisplayError, GetBotsByBotIdContainerDisplayErrors, GetBotsByBotIdContainerDisplayResponse, GetBotsByBotIdContainerDisplayResponses, GetBotsByBotIdContainerDisplaySessionsData, GetBotsByBotIdContainerDisplaySessionsError, GetBotsByBotIdContainerDisplaySessionsErrors, GetBotsByBotIdContainerDisplaySessionsResponse, GetBotsByBotIdContainerDisplaySessionsResponses, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerMetricsData, GetBotsByBotIdContainerMetricsError, GetBotsByBotIdContainerMetricsErrors, GetBotsByBotIdContainerMetricsResponse, GetBotsByBotIdContainerMetricsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdContainerTerminalData, GetBotsByBotIdContainerTerminalError, GetBotsByBotIdContainerTerminalErrors, GetBotsByBotIdContainerTerminalResponse, GetBotsByBotIdContainerTerminalResponses, GetBotsByBotIdContainerTerminalWsData, GetBotsByBotIdContainerTerminalWsError, GetBotsByBotIdContainerTerminalWsErrors, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsError, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponse, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdError, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponse, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxError, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponse, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdHooksEventsData, GetBotsByBotIdHooksEventsError, GetBotsByBotIdHooksEventsErrors, GetBotsByBotIdHooksEventsResponse, GetBotsByBotIdHooksEventsResponses, GetBotsByBotIdLocalStreamData, GetBotsByBotIdLocalStreamError, GetBotsByBotIdLocalStreamErrors, GetBotsByBotIdLocalStreamResponse, GetBotsByBotIdLocalStreamResponses, GetBotsByBotIdLocalWsData, GetBotsByBotIdLocalWsError, GetBotsByBotIdLocalWsErrors, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdOauthStatusData, GetBotsByBotIdMcpByIdOauthStatusError, GetBotsByBotIdMcpByIdOauthStatusErrors, GetBotsByBotIdMcpByIdOauthStatusResponse, GetBotsByBotIdMcpByIdOauthStatusResponses, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryStatusData, GetBotsByBotIdMemoryStatusError, GetBotsByBotIdMemoryStatusErrors, GetBotsByBotIdMemoryStatusResponse, GetBotsByBotIdMemoryStatusResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesLocateData, GetBotsByBotIdMessagesLocateError, GetBotsByBotIdMessagesLocateErrors, GetBotsByBotIdMessagesLocateResponse, GetBotsByBotIdMessagesLocateResponses, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdPluginsByIdData, GetBotsByBotIdPluginsByIdError, GetBotsByBotIdPluginsByIdErrors, GetBotsByBotIdPluginsByIdOauthStatusData, GetBotsByBotIdPluginsByIdOauthStatusError, GetBotsByBotIdPluginsByIdOauthStatusErrors, GetBotsByBotIdPluginsByIdOauthStatusResponse, GetBotsByBotIdPluginsByIdOauthStatusResponses, GetBotsByBotIdPluginsByIdResponse, GetBotsByBotIdPluginsByIdResponses, GetBotsByBotIdPluginsData, GetBotsByBotIdPluginsError, GetBotsByBotIdPluginsErrors, GetBotsByBotIdPluginsResponse, GetBotsByBotIdPluginsResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdLogsData, GetBotsByBotIdScheduleByIdLogsError, GetBotsByBotIdScheduleByIdLogsErrors, GetBotsByBotIdScheduleByIdLogsResponse, GetBotsByBotIdScheduleByIdLogsResponses, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleLogsData, GetBotsByBotIdScheduleLogsError, GetBotsByBotIdScheduleLogsErrors, GetBotsByBotIdScheduleLogsResponse, GetBotsByBotIdScheduleLogsResponses, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSessionsBySessionIdAcpRuntimeData, GetBotsByBotIdSessionsBySessionIdAcpRuntimeError, GetBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, GetBotsByBotIdSessionsBySessionIdData, GetBotsByBotIdSessionsBySessionIdError, GetBotsByBotIdSessionsBySessionIdErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsData, GetBotsByBotIdSessionsBySessionIdMessagesEventsError, GetBotsByBotIdSessionsBySessionIdMessagesEventsErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponse, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponses, GetBotsByBotIdSessionsBySessionIdResponse, GetBotsByBotIdSessionsBySessionIdResponses, GetBotsByBotIdSessionsBySessionIdStatusData, GetBotsByBotIdSessionsBySessionIdStatusError, GetBotsByBotIdSessionsBySessionIdStatusErrors, GetBotsByBotIdSessionsBySessionIdStatusResponse, GetBotsByBotIdSessionsBySessionIdStatusResponses, GetBotsByBotIdSessionsData, GetBotsByBotIdSessionsError, GetBotsByBotIdSessionsErrors, GetBotsByBotIdSessionsEventsData, GetBotsByBotIdSessionsEventsError, GetBotsByBotIdSessionsEventsErrors, GetBotsByBotIdSessionsEventsResponse, GetBotsByBotIdSessionsEventsResponses, GetBotsByBotIdSessionsResponse, GetBotsByBotIdSessionsResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdTokenUsageData, GetBotsByBotIdTokenUsageError, GetBotsByBotIdTokenUsageErrors, GetBotsByBotIdTokenUsageRecordsData, GetBotsByBotIdTokenUsageRecordsError, GetBotsByBotIdTokenUsageRecordsErrors, GetBotsByBotIdTokenUsageRecordsResponse, GetBotsByBotIdTokenUsageRecordsResponses, GetBotsByBotIdTokenUsageResponse, GetBotsByBotIdTokenUsageResponses, GetBotsByBotIdUserAccessCandidatesData, GetBotsByBotIdUserAccessCandidatesError, GetBotsByBotIdUserAccessCandidatesErrors, GetBotsByBotIdUserAccessCandidatesResponse, GetBotsByBotIdUserAccessCandidatesResponses, GetBotsByBotIdUserAccessData, GetBotsByBotIdUserAccessError, GetBotsByBotIdUserAccessErrors, GetBotsByBotIdUserAccessResponse, GetBotsByBotIdUserAccessResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsNameAvailabilityData, GetBotsNameAvailabilityError, GetBotsNameAvailabilityErrors, GetBotsNameAvailabilityResponse, GetBotsNameAvailabilityResponses, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetEmailOauthCallbackData, GetEmailOauthCallbackError, GetEmailOauthCallbackErrors, GetEmailOauthCallbackResponse, GetEmailOauthCallbackResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdError, GetEmailProvidersByIdErrors, GetEmailProvidersByIdOauthAuthorizeData, GetEmailProvidersByIdOauthAuthorizeError, GetEmailProvidersByIdOauthAuthorizeErrors, GetEmailProvidersByIdOauthAuthorizeResponse, GetEmailProvidersByIdOauthAuthorizeResponses, GetEmailProvidersByIdOauthStatusData, GetEmailProvidersByIdOauthStatusError, GetEmailProvidersByIdOauthStatusErrors, GetEmailProvidersByIdOauthStatusResponse, GetEmailProvidersByIdOauthStatusResponses, GetEmailProvidersByIdResponse, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersError, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponse, GetEmailProvidersMetaResponses, GetEmailProvidersResponse, GetEmailProvidersResponses, GetFetchProvidersByIdData, GetFetchProvidersByIdError, GetFetchProvidersByIdErrors, GetFetchProvidersByIdResponse, GetFetchProvidersByIdResponses, GetFetchProvidersData, GetFetchProvidersError, GetFetchProvidersErrors, GetFetchProvidersMetaData, GetFetchProvidersMetaResponse, GetFetchProvidersMetaResponses, GetFetchProvidersResponse, GetFetchProvidersResponses, GetMemoryProvidersByIdData, GetMemoryProvidersByIdError, GetMemoryProvidersByIdErrors, GetMemoryProvidersByIdResponse, GetMemoryProvidersByIdResponses, GetMemoryProvidersByIdStatusData, GetMemoryProvidersByIdStatusError, GetMemoryProvidersByIdStatusErrors, GetMemoryProvidersByIdStatusResponse, GetMemoryProvidersByIdStatusResponses, GetMemoryProvidersData, GetMemoryProvidersError, GetMemoryProvidersErrors, GetMemoryProvidersMetaData, GetMemoryProvidersMetaResponse, GetMemoryProvidersMetaResponses, GetMemoryProvidersResponse, GetMemoryProvidersResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetOauthMcpCallbackData, GetOauthMcpCallbackError, GetOauthMcpCallbackErrors, GetOauthMcpCallbackResponse, GetOauthMcpCallbackResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdOauthAuthorizeData, GetProvidersByIdOauthAuthorizeError, GetProvidersByIdOauthAuthorizeErrors, GetProvidersByIdOauthAuthorizeResponse, GetProvidersByIdOauthAuthorizeResponses, GetProvidersByIdOauthStatusData, GetProvidersByIdOauthStatusError, GetProvidersByIdOauthStatusErrors, GetProvidersByIdOauthStatusResponse, GetProvidersByIdOauthStatusResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersOauthCallbackData, GetProvidersOauthCallbackError, GetProvidersOauthCallbackErrors, GetProvidersOauthCallbackResponse, GetProvidersOauthCallbackResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetSpeechModelsByIdCapabilitiesData, GetSpeechModelsByIdCapabilitiesError, GetSpeechModelsByIdCapabilitiesErrors, GetSpeechModelsByIdCapabilitiesResponse, GetSpeechModelsByIdCapabilitiesResponses, GetSpeechModelsByIdData, GetSpeechModelsByIdError, GetSpeechModelsByIdErrors, GetSpeechModelsByIdResponse, GetSpeechModelsByIdResponses, GetSpeechModelsData, GetSpeechModelsError, GetSpeechModelsErrors, GetSpeechModelsResponse, GetSpeechModelsResponses, GetSpeechProvidersByIdData, GetSpeechProvidersByIdError, GetSpeechProvidersByIdErrors, GetSpeechProvidersByIdModelsData, GetSpeechProvidersByIdModelsError, GetSpeechProvidersByIdModelsErrors, GetSpeechProvidersByIdModelsResponse, GetSpeechProvidersByIdModelsResponses, GetSpeechProvidersByIdResponse, GetSpeechProvidersByIdResponses, GetSpeechProvidersData, GetSpeechProvidersError, GetSpeechProvidersErrors, GetSpeechProvidersMetaData, GetSpeechProvidersMetaResponse, GetSpeechProvidersMetaResponses, GetSpeechProvidersResponse, GetSpeechProvidersResponses, GetSupermarketPluginsByIdData, GetSupermarketPluginsByIdError, GetSupermarketPluginsByIdErrors, GetSupermarketPluginsByIdResponse, GetSupermarketPluginsByIdResponses, GetSupermarketPluginsData, GetSupermarketPluginsError, GetSupermarketPluginsErrors, GetSupermarketPluginsResponse, GetSupermarketPluginsResponses, GetSupermarketSkillsByIdData, GetSupermarketSkillsByIdError, GetSupermarketSkillsByIdErrors, GetSupermarketSkillsByIdResponse, GetSupermarketSkillsByIdResponses, GetSupermarketSkillsData, GetSupermarketSkillsError, GetSupermarketSkillsErrors, GetSupermarketSkillsResponse, GetSupermarketSkillsResponses, GetSupermarketTagsData, GetSupermarketTagsError, GetSupermarketTagsErrors, GetSupermarketTagsResponse, GetSupermarketTagsResponses, GetTranscriptionModelsByIdCapabilitiesData, GetTranscriptionModelsByIdCapabilitiesError, GetTranscriptionModelsByIdCapabilitiesErrors, GetTranscriptionModelsByIdCapabilitiesResponse, GetTranscriptionModelsByIdCapabilitiesResponses, GetTranscriptionModelsByIdData, GetTranscriptionModelsByIdError, GetTranscriptionModelsByIdErrors, GetTranscriptionModelsByIdResponse, GetTranscriptionModelsByIdResponses, GetTranscriptionModelsData, GetTranscriptionModelsError, GetTranscriptionModelsErrors, GetTranscriptionModelsResponse, GetTranscriptionModelsResponses, GetTranscriptionProvidersByIdData, GetTranscriptionProvidersByIdError, GetTranscriptionProvidersByIdErrors, GetTranscriptionProvidersByIdModelsData, GetTranscriptionProvidersByIdModelsError, GetTranscriptionProvidersByIdModelsErrors, GetTranscriptionProvidersByIdModelsResponse, GetTranscriptionProvidersByIdModelsResponses, GetTranscriptionProvidersByIdResponse, GetTranscriptionProvidersByIdResponses, GetTranscriptionProvidersData, GetTranscriptionProvidersError, GetTranscriptionProvidersErrors, GetTranscriptionProvidersMetaData, GetTranscriptionProvidersMetaResponse, GetTranscriptionProvidersMetaResponses, GetTranscriptionProvidersResponse, GetTranscriptionProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelIdentitiesData, GetUsersMeChannelIdentitiesError, GetUsersMeChannelIdentitiesErrors, GetUsersMeChannelIdentitiesResponse, GetUsersMeChannelIdentitiesResponses, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersAcpClaudeCodeOAuthAuthorizeResponse, HandlersAcpClaudeCodeOAuthExchangeRequest, HandlersAcpClaudeCodeOAuthStatus, HandlersAcpRuntimeCreateRequest, HandlersAcpRuntimeModelRequest, HandlersBatchDeleteRequest, HandlersBotUserCandidate, HandlersBotUserCandidateListResponse, HandlersBotUserGrantListResponse, HandlersBrowserSessionCreateRequest, HandlersBrowserSessionCreateResponse, HandlersBrowserSessionKeepAliveResponse, HandlersCacheStats, HandlersChannelMeta, HandlersContainerCpuMetricsResponse, HandlersContainerGpuRequest, HandlersContainerMemoryMetricsResponse, HandlersContainerMetricsPayloadResponse, HandlersContainerMetricsStatusResponse, HandlersContainerResourceLimitCapabilitiesResponse, HandlersContainerResourceLimitCapabilityResponse, HandlersContainerResourceLimitObservedResponse, HandlersContainerResourceLimitValuesResponse, HandlersContainerStorageMetricsResponse, HandlersContextUsage, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSessionRequest, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersDailyTokenUsage, HandlersDisplayInfoResponse, HandlersDisplaySessionListResponse, HandlersDisplayWebRtcOfferRequest, HandlersDisplayWebRtcOfferResponse, HandlersEmailOAuthStatusResponse, HandlersErrorResponse, HandlersFsArchiveRequest, HandlersFsDeleteRequest, HandlersFsExtractRequest, HandlersFsExtractResponse, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerMetricsResponse, HandlersGetContainerResourceLimitsResponse, HandlersGetContainerResponse, HandlersHookEventInfo, HandlersHooksEventsResponse, HandlersHookTestRequest, HandlersHookTestResponse, HandlersInstallPluginRequest, HandlersInstallSkillRequest, HandlersListSessionsResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersModelTokenUsage, HandlersOauthAuthorizeRequest, HandlersOauthDiscoverRequest, HandlersOauthExchangeRequest, HandlersPingResponse, HandlersProbeResponse, HandlersRefreshResponse, HandlersRollbackRequest, HandlersSessionInfoResponse, HandlersSkillItem, HandlersSkillsActionRequest, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HandlersSupermarketAuthor, HandlersSupermarketPluginListResponse, HandlersSupermarketSkillEntry, HandlersSupermarketSkillListResponse, HandlersSupermarketSkillMetadata, HandlersSupermarketTagsResponse, HandlersSynthesizeRequest, HandlersSynthesizeResponse, HandlersTerminalInfoResponse, HandlersTokenUsageRecord, HandlersTokenUsageRecordsResponse, HandlersTokenUsageResponse, HandlersToolApprovalDecisionRequest, HandlersTriggerCompactResponse, HandlersUpdateContainerMetricsRequest, HandlersUpdateContainerResourceLimitsRequest, HandlersUpdateSessionRequest, HeartbeatListLogsResponse, HeartbeatLog, HooksActionResult, HooksResult, HooksToolPayload, McpAuthorizeResult, McpDiscoveryResult, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpOAuthStatus, McpToolDescriptor, McpUpsertRequest, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsCountResponse, ModelsGetResponse, ModelsModelConfig, ModelsModelType, ModelsTestResponse, ModelsTestStatus, ModelsUpdateRequest, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelData, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelError, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelErrors, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponse, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponses, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelData, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelError, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelErrors, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponse, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponses, PatchBotsByBotIdSessionsBySessionIdData, PatchBotsByBotIdSessionsBySessionIdError, PatchBotsByBotIdSessionsBySessionIdErrors, PatchBotsByBotIdSessionsBySessionIdResponse, PatchBotsByBotIdSessionsBySessionIdResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PluginsAuthor, PluginsAuthRequirement, PluginsConfigVar, PluginsIcon, PluginsInstallation, PluginsListResponse, PluginsManifest, PluginsMcpResource, PluginsOAuthAuthorizeRequest, PluginsResource, PluginsSkillEntry, PluginsSkillResource, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsBackupImportData, PostBotsBackupImportError, PostBotsBackupImportErrors, PostBotsBackupImportPreviewData, PostBotsBackupImportPreviewError, PostBotsBackupImportPreviewErrors, PostBotsBackupImportPreviewResponse, PostBotsBackupImportPreviewResponses, PostBotsBackupImportResponse, PostBotsBackupImportResponses, PostBotsByBotIdAclRulesData, PostBotsByBotIdAclRulesError, PostBotsByBotIdAclRulesErrors, PostBotsByBotIdAclRulesResponse, PostBotsByBotIdAclRulesResponses, PostBotsByBotIdAcpClaudeCodeOauthExchangeData, PostBotsByBotIdAcpClaudeCodeOauthExchangeError, PostBotsByBotIdAcpClaudeCodeOauthExchangeErrors, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponse, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponses, PostBotsByBotIdAcpRuntimesData, PostBotsByBotIdAcpRuntimesError, PostBotsByBotIdAcpRuntimesErrors, PostBotsByBotIdAcpRuntimesResponse, PostBotsByBotIdAcpRuntimesResponses, PostBotsByBotIdBackupExportData, PostBotsByBotIdBackupExportError, PostBotsByBotIdBackupExportErrors, PostBotsByBotIdBackupExportResponses, PostBotsByBotIdChannelManagersData, PostBotsByBotIdChannelManagersError, PostBotsByBotIdChannelManagersErrors, PostBotsByBotIdChannelManagersResponses, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveData, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveError, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveErrors, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponse, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponses, PostBotsByBotIdContainerBrowserSessionsData, PostBotsByBotIdContainerBrowserSessionsError, PostBotsByBotIdContainerBrowserSessionsErrors, PostBotsByBotIdContainerBrowserSessionsResponse, PostBotsByBotIdContainerBrowserSessionsResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerDataRestoreData, PostBotsByBotIdContainerDataRestoreError, PostBotsByBotIdContainerDataRestoreErrors, PostBotsByBotIdContainerDataRestoreResponse, PostBotsByBotIdContainerDataRestoreResponses, PostBotsByBotIdContainerDisplayPrepareData, PostBotsByBotIdContainerDisplayPrepareError, PostBotsByBotIdContainerDisplayPrepareErrors, PostBotsByBotIdContainerDisplayPrepareResponse, PostBotsByBotIdContainerDisplayPrepareResponses, PostBotsByBotIdContainerDisplayWebrtcOfferData, PostBotsByBotIdContainerDisplayWebrtcOfferError, PostBotsByBotIdContainerDisplayWebrtcOfferErrors, PostBotsByBotIdContainerDisplayWebrtcOfferResponse, PostBotsByBotIdContainerDisplayWebrtcOfferResponses, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsArchiveData, PostBotsByBotIdContainerFsArchiveError, PostBotsByBotIdContainerFsArchiveErrors, PostBotsByBotIdContainerFsArchiveResponses, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsExtractData, PostBotsByBotIdContainerFsExtractError, PostBotsByBotIdContainerFsExtractErrors, PostBotsByBotIdContainerFsExtractResponse, PostBotsByBotIdContainerFsExtractResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsActionsData, PostBotsByBotIdContainerSkillsActionsError, PostBotsByBotIdContainerSkillsActionsErrors, PostBotsByBotIdContainerSkillsActionsResponse, PostBotsByBotIdContainerSkillsActionsResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerSnapshotsRollbackData, PostBotsByBotIdContainerSnapshotsRollbackError, PostBotsByBotIdContainerSnapshotsRollbackErrors, PostBotsByBotIdContainerSnapshotsRollbackResponse, PostBotsByBotIdContainerSnapshotsRollbackResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdHooksTestData, PostBotsByBotIdHooksTestError, PostBotsByBotIdHooksTestErrors, PostBotsByBotIdHooksTestResponse, PostBotsByBotIdHooksTestResponses, PostBotsByBotIdLocalMessagesData, PostBotsByBotIdLocalMessagesError, PostBotsByBotIdLocalMessagesErrors, PostBotsByBotIdLocalMessagesResponse, PostBotsByBotIdLocalMessagesResponses, PostBotsByBotIdMcpByIdOauthAuthorizeData, PostBotsByBotIdMcpByIdOauthAuthorizeError, PostBotsByBotIdMcpByIdOauthAuthorizeErrors, PostBotsByBotIdMcpByIdOauthAuthorizeResponse, PostBotsByBotIdMcpByIdOauthAuthorizeResponses, PostBotsByBotIdMcpByIdOauthDiscoverData, PostBotsByBotIdMcpByIdOauthDiscoverError, PostBotsByBotIdMcpByIdOauthDiscoverErrors, PostBotsByBotIdMcpByIdOauthDiscoverResponse, PostBotsByBotIdMcpByIdOauthDiscoverResponses, PostBotsByBotIdMcpByIdOauthExchangeData, PostBotsByBotIdMcpByIdOauthExchangeError, PostBotsByBotIdMcpByIdOauthExchangeErrors, PostBotsByBotIdMcpByIdOauthExchangeResponse, PostBotsByBotIdMcpByIdOauthExchangeResponses, PostBotsByBotIdMcpByIdProbeData, PostBotsByBotIdMcpByIdProbeError, PostBotsByBotIdMcpByIdProbeErrors, PostBotsByBotIdMcpByIdProbeResponse, PostBotsByBotIdMcpByIdProbeResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdPluginsByIdDisableData, PostBotsByBotIdPluginsByIdDisableError, PostBotsByBotIdPluginsByIdDisableErrors, PostBotsByBotIdPluginsByIdDisableResponse, PostBotsByBotIdPluginsByIdDisableResponses, PostBotsByBotIdPluginsByIdEnableData, PostBotsByBotIdPluginsByIdEnableError, PostBotsByBotIdPluginsByIdEnableErrors, PostBotsByBotIdPluginsByIdEnableResponse, PostBotsByBotIdPluginsByIdEnableResponses, PostBotsByBotIdPluginsByIdOauthAuthorizeData, PostBotsByBotIdPluginsByIdOauthAuthorizeError, PostBotsByBotIdPluginsByIdOauthAuthorizeErrors, PostBotsByBotIdPluginsByIdOauthAuthorizeResponse, PostBotsByBotIdPluginsByIdOauthAuthorizeResponses, PostBotsByBotIdPluginsByIdUninstallData, PostBotsByBotIdPluginsByIdUninstallError, PostBotsByBotIdPluginsByIdUninstallErrors, PostBotsByBotIdPluginsByIdUninstallResponse, PostBotsByBotIdPluginsByIdUninstallResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSessionsBySessionIdAcpRuntimeData, PostBotsByBotIdSessionsBySessionIdAcpRuntimeError, PostBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, PostBotsByBotIdSessionsBySessionIdCompactData, PostBotsByBotIdSessionsBySessionIdCompactError, PostBotsByBotIdSessionsBySessionIdCompactErrors, PostBotsByBotIdSessionsBySessionIdCompactResponse, PostBotsByBotIdSessionsBySessionIdCompactResponses, PostBotsByBotIdSessionsData, PostBotsByBotIdSessionsError, PostBotsByBotIdSessionsErrors, PostBotsByBotIdSessionsResponse, PostBotsByBotIdSessionsResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSupermarketInstallPluginData, PostBotsByBotIdSupermarketInstallPluginError, PostBotsByBotIdSupermarketInstallPluginErrors, PostBotsByBotIdSupermarketInstallPluginResponse, PostBotsByBotIdSupermarketInstallPluginResponses, PostBotsByBotIdSupermarketInstallSkillData, PostBotsByBotIdSupermarketInstallSkillError, PostBotsByBotIdSupermarketInstallSkillErrors, PostBotsByBotIdSupermarketInstallSkillResponse, PostBotsByBotIdSupermarketInstallSkillResponses, PostBotsByBotIdToolApprovalsByApprovalIdApproveData, PostBotsByBotIdToolApprovalsByApprovalIdApproveError, PostBotsByBotIdToolApprovalsByApprovalIdApproveErrors, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponse, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponses, PostBotsByBotIdToolApprovalsByApprovalIdRejectData, PostBotsByBotIdToolApprovalsByApprovalIdRejectError, PostBotsByBotIdToolApprovalsByApprovalIdRejectErrors, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponse, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdTtsSynthesizeData, PostBotsByBotIdTtsSynthesizeError, PostBotsByBotIdTtsSynthesizeErrors, PostBotsByBotIdTtsSynthesizeResponse, PostBotsByBotIdTtsSynthesizeResponses, PostBotsByBotIdUserAccessData, PostBotsByBotIdUserAccessError, PostBotsByBotIdUserAccessErrors, PostBotsByBotIdUserAccessResponse, PostBotsByBotIdUserAccessResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponse, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersErrors, PostEmailProvidersResponse, PostEmailProvidersResponses, PostFetchProvidersData, PostFetchProvidersError, PostFetchProvidersErrors, PostFetchProvidersResponse, PostFetchProvidersResponses, PostMemoryProvidersData, PostMemoryProvidersError, PostMemoryProvidersErrors, PostMemoryProvidersResponse, PostMemoryProvidersResponses, PostModelsByIdTestData, PostModelsByIdTestError, PostModelsByIdTestErrors, PostModelsByIdTestResponse, PostModelsByIdTestResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdImportModelsData, PostProvidersByIdImportModelsError, PostProvidersByIdImportModelsErrors, PostProvidersByIdImportModelsResponse, PostProvidersByIdImportModelsResponses, PostProvidersByIdOauthPollData, PostProvidersByIdOauthPollError, PostProvidersByIdOauthPollErrors, PostProvidersByIdOauthPollResponse, PostProvidersByIdOauthPollResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostSpeechModelsByIdTestData, PostSpeechModelsByIdTestError, PostSpeechModelsByIdTestErrors, PostSpeechModelsByIdTestResponses, PostSpeechProvidersByIdImportModelsData, PostSpeechProvidersByIdImportModelsError, PostSpeechProvidersByIdImportModelsErrors, PostSpeechProvidersByIdImportModelsResponse, PostSpeechProvidersByIdImportModelsResponses, PostTranscriptionModelsByIdTestData, PostTranscriptionModelsByIdTestError, PostTranscriptionModelsByIdTestErrors, PostTranscriptionModelsByIdTestResponse, PostTranscriptionModelsByIdTestResponses, PostTranscriptionProvidersByIdImportModelsData, PostTranscriptionProvidersByIdImportModelsError, PostTranscriptionProvidersByIdImportModelsErrors, PostTranscriptionProvidersByIdImportModelsResponse, PostTranscriptionProvidersByIdImportModelsResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersMeChannelLinksData, PostUsersMeChannelLinksError, PostUsersMeChannelLinksErrors, PostUsersMeChannelLinksResponse, PostUsersMeChannelLinksResponses, PostUsersResponse, PostUsersResponses, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersImportModelsResponse, ProvidersOAuthAccount, ProvidersOAuthAuthorizeResponse, ProvidersOAuthDeviceStatus, ProvidersOAuthStatus, ProvidersTestResponse, ProvidersTestStatus, ProvidersUpdateRequest, PutBotsByBotIdAclDefaultEffectData, PutBotsByBotIdAclDefaultEffectError, PutBotsByBotIdAclDefaultEffectErrors, PutBotsByBotIdAclDefaultEffectResponses, PutBotsByBotIdAclRulesByRuleIdData, PutBotsByBotIdAclRulesByRuleIdError, PutBotsByBotIdAclRulesByRuleIdErrors, PutBotsByBotIdAclRulesByRuleIdResponse, PutBotsByBotIdAclRulesByRuleIdResponses, PutBotsByBotIdContainerMetricsData, PutBotsByBotIdContainerMetricsError, PutBotsByBotIdContainerMetricsErrors, PutBotsByBotIdContainerMetricsResponse, PutBotsByBotIdContainerMetricsResponses, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdUserAccessByGrantIdData, PutBotsByBotIdUserAccessByGrantIdError, PutBotsByBotIdUserAccessByGrantIdErrors, PutBotsByBotIdUserAccessByGrantIdResponse, PutBotsByBotIdUserAccessByGrantIdResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponse, PutEmailProvidersByIdResponses, PutFetchProvidersByIdData, PutFetchProvidersByIdError, PutFetchProvidersByIdErrors, PutFetchProvidersByIdResponse, PutFetchProvidersByIdResponses, PutMemoryProvidersByIdData, PutMemoryProvidersByIdError, PutMemoryProvidersByIdErrors, PutMemoryProvidersByIdResponse, PutMemoryProvidersByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutSpeechModelsByIdData, PutSpeechModelsByIdError, PutSpeechModelsByIdErrors, PutSpeechModelsByIdResponse, PutSpeechModelsByIdResponses, PutTranscriptionModelsByIdData, PutTranscriptionModelsByIdError, PutTranscriptionModelsByIdErrors, PutTranscriptionModelsByIdResponse, PutTranscriptionModelsByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListLogsResponse, ScheduleListResponse, ScheduleLog, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SessionSession, SettingsSettings, SettingsToolApprovalConfig, SettingsToolApprovalExecPolicy, SettingsToolApprovalFilePolicy, SettingsUpsertRequest } from './types.gen'; +export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileMetadata, AccountsUpdateProfileRequest, AclChannelIdentityCandidate, AclChannelIdentityCandidateListResponse, AclCreateRuleRequest, AclDefaultEffectResponse, AclListRulesResponse, AclObservedConversationCandidate, AclObservedConversationCandidateListResponse, AclRule, AclSourceScope, AclUpdateRuleRequest, AcpagentRuntimeStatus, AcpclientModelInfo, AcpclientModelState, AcpprofileManagedField, AcpprofileProfilesResponse, AcpprofilePublicProfile, AdaptersCompactResult, AdaptersDeleteResponse, AdaptersHealthStatus, AdaptersMemoryCompactCapability, AdaptersMemoryItem, AdaptersMemoryStatusResponse, AdaptersMessage, AdaptersProviderCollectionStatus, AdaptersProviderConfigSchema, AdaptersProviderCreateRequest, AdaptersProviderFieldSchema, AdaptersProviderGetResponse, AdaptersProviderMeta, AdaptersProviderStatusResponse, AdaptersProviderType, AdaptersProviderUpdateRequest, AdaptersRebuildResult, AdaptersSearchResponse, AdaptersUsageResponse, AudioConfigSchema, AudioFieldSchema, AudioImportModelsResponse, AudioModelCapabilities, AudioModelInfo, AudioParamConstraint, AudioProviderMetaResponse, AudioSpeechModelResponse, AudioSpeechProviderResponse, AudioTestSynthesizeRequest, AudioTestTranscriptionResponse, AudioTranscriptionModelResponse, AudioTranscriptionWord, AudioUpdateSpeechModelRequest, AudioVoiceInfo, BotbackupExportRequest, BotbackupImportMode, BotbackupImportResult, BotbackupManifest, BotbackupManifestEntry, BotbackupManifestOptions, BotbackupPreviewResult, BotbackupProfilePreview, BotbackupRestorePlan, BotbackupSection, BotbackupSectionSummary, BotbackupSummaryResult, BotsBot, BotsBotCheck, BotsCreateBotRequest, BotsCreateUserGrantRequest, BotsListBotsResponse, BotsListChecksResponse, BotsNameAvailability, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpdateUserGrantRequest, BotsUserGrant, ChannelaccessBinding, ChannelaccessIssueLinkCodeRequest, ChannelaccessLinkCode, ChannelaccessListBindingsResponse, ChannelaccessListManagersResponse, ChannelaccessManager, ChannelaccessSetManagerRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelChannelType, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelForwardRef, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, CompactionListLogsResponse, CompactionLog, DeleteBotsByBotIdAclRulesByRuleIdData, DeleteBotsByBotIdAclRulesByRuleIdError, DeleteBotsByBotIdAclRulesByRuleIdErrors, DeleteBotsByBotIdAclRulesByRuleIdResponses, DeleteBotsByBotIdAcpRuntimesByRuntimeIdData, DeleteBotsByBotIdAcpRuntimesByRuntimeIdError, DeleteBotsByBotIdAcpRuntimesByRuntimeIdErrors, DeleteBotsByBotIdAcpRuntimesByRuntimeIdResponses, DeleteBotsByBotIdChannelManagersByChannelIdentityIdData, DeleteBotsByBotIdChannelManagersByChannelIdentityIdError, DeleteBotsByBotIdChannelManagersByChannelIdentityIdErrors, DeleteBotsByBotIdChannelManagersByChannelIdentityIdResponses, DeleteBotsByBotIdCompactionLogsData, DeleteBotsByBotIdCompactionLogsError, DeleteBotsByBotIdCompactionLogsErrors, DeleteBotsByBotIdCompactionLogsResponses, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdData, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdError, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdErrors, DeleteBotsByBotIdContainerBrowserSessionsBySessionIdResponses, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdData, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdError, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdErrors, DeleteBotsByBotIdContainerDisplaySessionsBySessionIdResponses, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdOauthTokenData, DeleteBotsByBotIdMcpByIdOauthTokenError, DeleteBotsByBotIdMcpByIdOauthTokenErrors, DeleteBotsByBotIdMcpByIdOauthTokenResponses, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdPluginsByIdData, DeleteBotsByBotIdPluginsByIdError, DeleteBotsByBotIdPluginsByIdErrors, DeleteBotsByBotIdPluginsByIdResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdScheduleLogsData, DeleteBotsByBotIdScheduleLogsError, DeleteBotsByBotIdScheduleLogsErrors, DeleteBotsByBotIdScheduleLogsResponses, DeleteBotsByBotIdSessionsBySessionIdData, DeleteBotsByBotIdSessionsBySessionIdError, DeleteBotsByBotIdSessionsBySessionIdErrors, DeleteBotsByBotIdSessionsBySessionIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdUserAccessByGrantIdData, DeleteBotsByBotIdUserAccessByGrantIdError, DeleteBotsByBotIdUserAccessByGrantIdErrors, DeleteBotsByBotIdUserAccessByGrantIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdOauthTokenData, DeleteEmailProvidersByIdOauthTokenError, DeleteEmailProvidersByIdOauthTokenErrors, DeleteEmailProvidersByIdOauthTokenResponses, DeleteEmailProvidersByIdResponses, DeleteFetchProvidersByIdData, DeleteFetchProvidersByIdError, DeleteFetchProvidersByIdErrors, DeleteFetchProvidersByIdResponses, DeleteMemoryProvidersByIdData, DeleteMemoryProvidersByIdError, DeleteMemoryProvidersByIdErrors, DeleteMemoryProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdOauthTokenData, DeleteProvidersByIdOauthTokenError, DeleteProvidersByIdOauthTokenErrors, DeleteProvidersByIdOauthTokenResponses, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, DeleteUsersByIdData, DeleteUsersByIdError, DeleteUsersByIdErrors, DeleteUsersByIdResponses, DeleteUsersMeChannelIdentitiesByChannelIdentityIdData, DeleteUsersMeChannelIdentitiesByChannelIdentityIdError, DeleteUsersMeChannelIdentitiesByChannelIdentityIdErrors, DeleteUsersMeChannelIdentitiesByChannelIdentityIdResponses, DisplaySessionInfo, EmailBindingResponse, EmailConfigSchema, EmailCreateBindingRequest, EmailCreateProviderRequest, EmailFieldSchema, EmailOutboxItemResponse, EmailProviderMeta, EmailProviderResponse, EmailUpdateBindingRequest, EmailUpdateProviderRequest, FetchprovidersCreateRequest, FetchprovidersGetResponse, FetchprovidersProviderConfigSchema, FetchprovidersProviderFieldSchema, FetchprovidersProviderMeta, FetchprovidersProviderName, FetchprovidersUpdateRequest, GetAcpProfilesData, GetAcpProfilesResponse, GetAcpProfilesResponses, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsData, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsError, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsErrors, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponse, GetBotsByBotIdAclChannelIdentitiesByChannelIdentityIdConversationsResponses, GetBotsByBotIdAclChannelIdentitiesData, GetBotsByBotIdAclChannelIdentitiesError, GetBotsByBotIdAclChannelIdentitiesErrors, GetBotsByBotIdAclChannelIdentitiesResponse, GetBotsByBotIdAclChannelIdentitiesResponses, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsData, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsError, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsErrors, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponse, GetBotsByBotIdAclChannelTypesByChannelTypeConversationsResponses, GetBotsByBotIdAclDefaultEffectData, GetBotsByBotIdAclDefaultEffectError, GetBotsByBotIdAclDefaultEffectErrors, GetBotsByBotIdAclDefaultEffectResponse, GetBotsByBotIdAclDefaultEffectResponses, GetBotsByBotIdAclRulesData, GetBotsByBotIdAclRulesError, GetBotsByBotIdAclRulesErrors, GetBotsByBotIdAclRulesResponse, GetBotsByBotIdAclRulesResponses, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeData, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeError, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeErrors, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponse, GetBotsByBotIdAcpClaudeCodeOauthAuthorizeResponses, GetBotsByBotIdAcpClaudeCodeOauthStatusData, GetBotsByBotIdAcpClaudeCodeOauthStatusError, GetBotsByBotIdAcpClaudeCodeOauthStatusErrors, GetBotsByBotIdAcpClaudeCodeOauthStatusResponse, GetBotsByBotIdAcpClaudeCodeOauthStatusResponses, GetBotsByBotIdAcpRuntimesByRuntimeIdData, GetBotsByBotIdAcpRuntimesByRuntimeIdError, GetBotsByBotIdAcpRuntimesByRuntimeIdErrors, GetBotsByBotIdAcpRuntimesByRuntimeIdResponse, GetBotsByBotIdAcpRuntimesByRuntimeIdResponses, GetBotsByBotIdBackupSummaryData, GetBotsByBotIdBackupSummaryError, GetBotsByBotIdBackupSummaryErrors, GetBotsByBotIdBackupSummaryResponse, GetBotsByBotIdBackupSummaryResponses, GetBotsByBotIdChannelManagersData, GetBotsByBotIdChannelManagersError, GetBotsByBotIdChannelManagersErrors, GetBotsByBotIdChannelManagersResponse, GetBotsByBotIdChannelManagersResponses, GetBotsByBotIdCompactionLogsData, GetBotsByBotIdCompactionLogsError, GetBotsByBotIdCompactionLogsErrors, GetBotsByBotIdCompactionLogsResponse, GetBotsByBotIdCompactionLogsResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerDisplayData, GetBotsByBotIdContainerDisplayError, GetBotsByBotIdContainerDisplayErrors, GetBotsByBotIdContainerDisplayResponse, GetBotsByBotIdContainerDisplayResponses, GetBotsByBotIdContainerDisplaySessionsData, GetBotsByBotIdContainerDisplaySessionsError, GetBotsByBotIdContainerDisplaySessionsErrors, GetBotsByBotIdContainerDisplaySessionsResponse, GetBotsByBotIdContainerDisplaySessionsResponses, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerMetricsData, GetBotsByBotIdContainerMetricsError, GetBotsByBotIdContainerMetricsErrors, GetBotsByBotIdContainerMetricsResponse, GetBotsByBotIdContainerMetricsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdContainerTerminalData, GetBotsByBotIdContainerTerminalError, GetBotsByBotIdContainerTerminalErrors, GetBotsByBotIdContainerTerminalResponse, GetBotsByBotIdContainerTerminalResponses, GetBotsByBotIdContainerTerminalWsData, GetBotsByBotIdContainerTerminalWsError, GetBotsByBotIdContainerTerminalWsErrors, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsError, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponse, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdError, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponse, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxError, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponse, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdHooksEventsData, GetBotsByBotIdHooksEventsError, GetBotsByBotIdHooksEventsErrors, GetBotsByBotIdHooksEventsResponse, GetBotsByBotIdHooksEventsResponses, GetBotsByBotIdLocalStreamData, GetBotsByBotIdLocalStreamError, GetBotsByBotIdLocalStreamErrors, GetBotsByBotIdLocalStreamResponse, GetBotsByBotIdLocalStreamResponses, GetBotsByBotIdLocalWsData, GetBotsByBotIdLocalWsError, GetBotsByBotIdLocalWsErrors, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdOauthStatusData, GetBotsByBotIdMcpByIdOauthStatusError, GetBotsByBotIdMcpByIdOauthStatusErrors, GetBotsByBotIdMcpByIdOauthStatusResponse, GetBotsByBotIdMcpByIdOauthStatusResponses, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryStatusData, GetBotsByBotIdMemoryStatusError, GetBotsByBotIdMemoryStatusErrors, GetBotsByBotIdMemoryStatusResponse, GetBotsByBotIdMemoryStatusResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesLocateData, GetBotsByBotIdMessagesLocateError, GetBotsByBotIdMessagesLocateErrors, GetBotsByBotIdMessagesLocateResponse, GetBotsByBotIdMessagesLocateResponses, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdPluginsByIdData, GetBotsByBotIdPluginsByIdError, GetBotsByBotIdPluginsByIdErrors, GetBotsByBotIdPluginsByIdOauthStatusData, GetBotsByBotIdPluginsByIdOauthStatusError, GetBotsByBotIdPluginsByIdOauthStatusErrors, GetBotsByBotIdPluginsByIdOauthStatusResponse, GetBotsByBotIdPluginsByIdOauthStatusResponses, GetBotsByBotIdPluginsByIdResponse, GetBotsByBotIdPluginsByIdResponses, GetBotsByBotIdPluginsData, GetBotsByBotIdPluginsError, GetBotsByBotIdPluginsErrors, GetBotsByBotIdPluginsResponse, GetBotsByBotIdPluginsResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdLogsData, GetBotsByBotIdScheduleByIdLogsError, GetBotsByBotIdScheduleByIdLogsErrors, GetBotsByBotIdScheduleByIdLogsResponse, GetBotsByBotIdScheduleByIdLogsResponses, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleLogsData, GetBotsByBotIdScheduleLogsError, GetBotsByBotIdScheduleLogsErrors, GetBotsByBotIdScheduleLogsResponse, GetBotsByBotIdScheduleLogsResponses, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSessionsBySessionIdAcpRuntimeData, GetBotsByBotIdSessionsBySessionIdAcpRuntimeError, GetBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, GetBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, GetBotsByBotIdSessionsBySessionIdData, GetBotsByBotIdSessionsBySessionIdError, GetBotsByBotIdSessionsBySessionIdErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsData, GetBotsByBotIdSessionsBySessionIdMessagesEventsError, GetBotsByBotIdSessionsBySessionIdMessagesEventsErrors, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponse, GetBotsByBotIdSessionsBySessionIdMessagesEventsResponses, GetBotsByBotIdSessionsBySessionIdResponse, GetBotsByBotIdSessionsBySessionIdResponses, GetBotsByBotIdSessionsBySessionIdStatusData, GetBotsByBotIdSessionsBySessionIdStatusError, GetBotsByBotIdSessionsBySessionIdStatusErrors, GetBotsByBotIdSessionsBySessionIdStatusResponse, GetBotsByBotIdSessionsBySessionIdStatusResponses, GetBotsByBotIdSessionsData, GetBotsByBotIdSessionsError, GetBotsByBotIdSessionsErrors, GetBotsByBotIdSessionsEventsData, GetBotsByBotIdSessionsEventsError, GetBotsByBotIdSessionsEventsErrors, GetBotsByBotIdSessionsEventsResponse, GetBotsByBotIdSessionsEventsResponses, GetBotsByBotIdSessionsResponse, GetBotsByBotIdSessionsResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdTokenUsageData, GetBotsByBotIdTokenUsageError, GetBotsByBotIdTokenUsageErrors, GetBotsByBotIdTokenUsageRecordsData, GetBotsByBotIdTokenUsageRecordsError, GetBotsByBotIdTokenUsageRecordsErrors, GetBotsByBotIdTokenUsageRecordsResponse, GetBotsByBotIdTokenUsageRecordsResponses, GetBotsByBotIdTokenUsageResponse, GetBotsByBotIdTokenUsageResponses, GetBotsByBotIdUserAccessCandidatesData, GetBotsByBotIdUserAccessCandidatesError, GetBotsByBotIdUserAccessCandidatesErrors, GetBotsByBotIdUserAccessCandidatesResponse, GetBotsByBotIdUserAccessCandidatesResponses, GetBotsByBotIdUserAccessData, GetBotsByBotIdUserAccessError, GetBotsByBotIdUserAccessErrors, GetBotsByBotIdUserAccessResponse, GetBotsByBotIdUserAccessResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsNameAvailabilityData, GetBotsNameAvailabilityError, GetBotsNameAvailabilityErrors, GetBotsNameAvailabilityResponse, GetBotsNameAvailabilityResponses, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetEmailOauthCallbackData, GetEmailOauthCallbackError, GetEmailOauthCallbackErrors, GetEmailOauthCallbackResponse, GetEmailOauthCallbackResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdError, GetEmailProvidersByIdErrors, GetEmailProvidersByIdOauthAuthorizeData, GetEmailProvidersByIdOauthAuthorizeError, GetEmailProvidersByIdOauthAuthorizeErrors, GetEmailProvidersByIdOauthAuthorizeResponse, GetEmailProvidersByIdOauthAuthorizeResponses, GetEmailProvidersByIdOauthStatusData, GetEmailProvidersByIdOauthStatusError, GetEmailProvidersByIdOauthStatusErrors, GetEmailProvidersByIdOauthStatusResponse, GetEmailProvidersByIdOauthStatusResponses, GetEmailProvidersByIdResponse, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersError, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponse, GetEmailProvidersMetaResponses, GetEmailProvidersResponse, GetEmailProvidersResponses, GetFetchProvidersByIdData, GetFetchProvidersByIdError, GetFetchProvidersByIdErrors, GetFetchProvidersByIdResponse, GetFetchProvidersByIdResponses, GetFetchProvidersData, GetFetchProvidersError, GetFetchProvidersErrors, GetFetchProvidersMetaData, GetFetchProvidersMetaResponse, GetFetchProvidersMetaResponses, GetFetchProvidersResponse, GetFetchProvidersResponses, GetMemoryProvidersByIdData, GetMemoryProvidersByIdError, GetMemoryProvidersByIdErrors, GetMemoryProvidersByIdResponse, GetMemoryProvidersByIdResponses, GetMemoryProvidersByIdStatusData, GetMemoryProvidersByIdStatusError, GetMemoryProvidersByIdStatusErrors, GetMemoryProvidersByIdStatusResponse, GetMemoryProvidersByIdStatusResponses, GetMemoryProvidersData, GetMemoryProvidersError, GetMemoryProvidersErrors, GetMemoryProvidersMetaData, GetMemoryProvidersMetaResponse, GetMemoryProvidersMetaResponses, GetMemoryProvidersResponse, GetMemoryProvidersResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetOauthMcpCallbackData, GetOauthMcpCallbackError, GetOauthMcpCallbackErrors, GetOauthMcpCallbackResponse, GetOauthMcpCallbackResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdOauthAuthorizeData, GetProvidersByIdOauthAuthorizeError, GetProvidersByIdOauthAuthorizeErrors, GetProvidersByIdOauthAuthorizeResponse, GetProvidersByIdOauthAuthorizeResponses, GetProvidersByIdOauthStatusData, GetProvidersByIdOauthStatusError, GetProvidersByIdOauthStatusErrors, GetProvidersByIdOauthStatusResponse, GetProvidersByIdOauthStatusResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersOauthCallbackData, GetProvidersOauthCallbackError, GetProvidersOauthCallbackErrors, GetProvidersOauthCallbackResponse, GetProvidersOauthCallbackResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetSpeechModelsByIdCapabilitiesData, GetSpeechModelsByIdCapabilitiesError, GetSpeechModelsByIdCapabilitiesErrors, GetSpeechModelsByIdCapabilitiesResponse, GetSpeechModelsByIdCapabilitiesResponses, GetSpeechModelsByIdData, GetSpeechModelsByIdError, GetSpeechModelsByIdErrors, GetSpeechModelsByIdResponse, GetSpeechModelsByIdResponses, GetSpeechModelsData, GetSpeechModelsError, GetSpeechModelsErrors, GetSpeechModelsResponse, GetSpeechModelsResponses, GetSpeechProvidersByIdData, GetSpeechProvidersByIdError, GetSpeechProvidersByIdErrors, GetSpeechProvidersByIdModelsData, GetSpeechProvidersByIdModelsError, GetSpeechProvidersByIdModelsErrors, GetSpeechProvidersByIdModelsResponse, GetSpeechProvidersByIdModelsResponses, GetSpeechProvidersByIdResponse, GetSpeechProvidersByIdResponses, GetSpeechProvidersData, GetSpeechProvidersError, GetSpeechProvidersErrors, GetSpeechProvidersMetaData, GetSpeechProvidersMetaResponse, GetSpeechProvidersMetaResponses, GetSpeechProvidersResponse, GetSpeechProvidersResponses, GetSupermarketPluginsByIdData, GetSupermarketPluginsByIdError, GetSupermarketPluginsByIdErrors, GetSupermarketPluginsByIdResponse, GetSupermarketPluginsByIdResponses, GetSupermarketPluginsData, GetSupermarketPluginsError, GetSupermarketPluginsErrors, GetSupermarketPluginsResponse, GetSupermarketPluginsResponses, GetSupermarketSkillsByIdData, GetSupermarketSkillsByIdError, GetSupermarketSkillsByIdErrors, GetSupermarketSkillsByIdResponse, GetSupermarketSkillsByIdResponses, GetSupermarketSkillsData, GetSupermarketSkillsError, GetSupermarketSkillsErrors, GetSupermarketSkillsResponse, GetSupermarketSkillsResponses, GetSupermarketTagsData, GetSupermarketTagsError, GetSupermarketTagsErrors, GetSupermarketTagsResponse, GetSupermarketTagsResponses, GetTranscriptionModelsByIdCapabilitiesData, GetTranscriptionModelsByIdCapabilitiesError, GetTranscriptionModelsByIdCapabilitiesErrors, GetTranscriptionModelsByIdCapabilitiesResponse, GetTranscriptionModelsByIdCapabilitiesResponses, GetTranscriptionModelsByIdData, GetTranscriptionModelsByIdError, GetTranscriptionModelsByIdErrors, GetTranscriptionModelsByIdResponse, GetTranscriptionModelsByIdResponses, GetTranscriptionModelsData, GetTranscriptionModelsError, GetTranscriptionModelsErrors, GetTranscriptionModelsResponse, GetTranscriptionModelsResponses, GetTranscriptionProvidersByIdData, GetTranscriptionProvidersByIdError, GetTranscriptionProvidersByIdErrors, GetTranscriptionProvidersByIdModelsData, GetTranscriptionProvidersByIdModelsError, GetTranscriptionProvidersByIdModelsErrors, GetTranscriptionProvidersByIdModelsResponse, GetTranscriptionProvidersByIdModelsResponses, GetTranscriptionProvidersByIdResponse, GetTranscriptionProvidersByIdResponses, GetTranscriptionProvidersData, GetTranscriptionProvidersError, GetTranscriptionProvidersErrors, GetTranscriptionProvidersMetaData, GetTranscriptionProvidersMetaResponse, GetTranscriptionProvidersMetaResponses, GetTranscriptionProvidersResponse, GetTranscriptionProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelIdentitiesData, GetUsersMeChannelIdentitiesError, GetUsersMeChannelIdentitiesErrors, GetUsersMeChannelIdentitiesResponse, GetUsersMeChannelIdentitiesResponses, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersAcpClaudeCodeOAuthAuthorizeResponse, HandlersAcpClaudeCodeOAuthExchangeRequest, HandlersAcpClaudeCodeOAuthStatus, HandlersAcpRuntimeCreateRequest, HandlersAcpRuntimeModelRequest, HandlersBatchDeleteRequest, HandlersBotUserCandidate, HandlersBotUserCandidateListResponse, HandlersBotUserGrantListResponse, HandlersBrowserSessionCreateRequest, HandlersBrowserSessionCreateResponse, HandlersBrowserSessionKeepAliveResponse, HandlersCacheStats, HandlersChannelMeta, HandlersContainerCpuMetricsResponse, HandlersContainerGpuRequest, HandlersContainerMemoryMetricsResponse, HandlersContainerMetricsPayloadResponse, HandlersContainerMetricsStatusResponse, HandlersContainerResourceLimitCapabilitiesResponse, HandlersContainerResourceLimitCapabilityResponse, HandlersContainerResourceLimitObservedResponse, HandlersContainerResourceLimitValuesResponse, HandlersContainerStorageMetricsResponse, HandlersContextUsage, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSessionRequest, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersDailyTokenUsage, HandlersDisplayInfoResponse, HandlersDisplaySessionListResponse, HandlersDisplayWebRtcOfferRequest, HandlersDisplayWebRtcOfferResponse, HandlersEmailOAuthStatusResponse, HandlersErrorResponse, HandlersFsArchiveRequest, HandlersFsDeleteRequest, HandlersFsExtractRequest, HandlersFsExtractResponse, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerMetricsResponse, HandlersGetContainerResourceLimitsResponse, HandlersGetContainerResponse, HandlersHookEventInfo, HandlersHooksEventsResponse, HandlersHookTestRequest, HandlersHookTestResponse, HandlersInstallPluginRequest, HandlersInstallSkillRequest, HandlersListSessionsResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersModelTokenUsage, HandlersOauthAuthorizeRequest, HandlersOauthDiscoverRequest, HandlersOauthExchangeRequest, HandlersPingResponse, HandlersProbeResponse, HandlersRefreshResponse, HandlersRollbackRequest, HandlersSessionInfoResponse, HandlersSkillItem, HandlersSkillsActionRequest, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HandlersSupermarketAuthor, HandlersSupermarketPluginListResponse, HandlersSupermarketSkillEntry, HandlersSupermarketSkillListResponse, HandlersSupermarketSkillMetadata, HandlersSupermarketTagsResponse, HandlersSynthesizeRequest, HandlersSynthesizeResponse, HandlersTerminalInfoResponse, HandlersTokenUsageRecord, HandlersTokenUsageRecordsResponse, HandlersTokenUsageResponse, HandlersToolApprovalDecisionRequest, HandlersTriggerCompactResponse, HandlersUpdateContainerMetricsRequest, HandlersUpdateContainerResourceLimitsRequest, HandlersUpdateSessionRequest, HeartbeatListLogsResponse, HeartbeatLog, HooksActionResult, HooksResult, HooksToolPayload, McpAuthorizeResult, McpDiscoveryResult, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpOAuthStatus, McpToolDescriptor, McpUpsertRequest, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsCountResponse, ModelsGetResponse, ModelsModelConfig, ModelsModelType, ModelsTestResponse, ModelsTestStatus, ModelsUpdateRequest, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelData, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelError, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelErrors, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponse, PatchBotsByBotIdAcpRuntimesByRuntimeIdModelResponses, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelData, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelError, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelErrors, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponse, PatchBotsByBotIdSessionsBySessionIdAcpRuntimeModelResponses, PatchBotsByBotIdSessionsBySessionIdData, PatchBotsByBotIdSessionsBySessionIdError, PatchBotsByBotIdSessionsBySessionIdErrors, PatchBotsByBotIdSessionsBySessionIdResponse, PatchBotsByBotIdSessionsBySessionIdResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PluginsAuthor, PluginsAuthRequirement, PluginsConfigVar, PluginsIcon, PluginsInstallation, PluginsListResponse, PluginsManifest, PluginsMcpResource, PluginsOAuthAuthorizeRequest, PluginsResource, PluginsSkillEntry, PluginsSkillResource, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsBackupImportData, PostBotsBackupImportError, PostBotsBackupImportErrors, PostBotsBackupImportPreviewData, PostBotsBackupImportPreviewError, PostBotsBackupImportPreviewErrors, PostBotsBackupImportPreviewResponse, PostBotsBackupImportPreviewResponses, PostBotsBackupImportResponse, PostBotsBackupImportResponses, PostBotsByBotIdAclRulesData, PostBotsByBotIdAclRulesError, PostBotsByBotIdAclRulesErrors, PostBotsByBotIdAclRulesResponse, PostBotsByBotIdAclRulesResponses, PostBotsByBotIdAcpClaudeCodeOauthExchangeData, PostBotsByBotIdAcpClaudeCodeOauthExchangeError, PostBotsByBotIdAcpClaudeCodeOauthExchangeErrors, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponse, PostBotsByBotIdAcpClaudeCodeOauthExchangeResponses, PostBotsByBotIdAcpRuntimesData, PostBotsByBotIdAcpRuntimesError, PostBotsByBotIdAcpRuntimesErrors, PostBotsByBotIdAcpRuntimesResponse, PostBotsByBotIdAcpRuntimesResponses, PostBotsByBotIdBackupExportData, PostBotsByBotIdBackupExportError, PostBotsByBotIdBackupExportErrors, PostBotsByBotIdBackupExportResponses, PostBotsByBotIdChannelManagersData, PostBotsByBotIdChannelManagersError, PostBotsByBotIdChannelManagersErrors, PostBotsByBotIdChannelManagersResponses, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveData, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveError, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveErrors, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponse, PostBotsByBotIdContainerBrowserSessionsBySessionIdKeepaliveResponses, PostBotsByBotIdContainerBrowserSessionsData, PostBotsByBotIdContainerBrowserSessionsError, PostBotsByBotIdContainerBrowserSessionsErrors, PostBotsByBotIdContainerBrowserSessionsResponse, PostBotsByBotIdContainerBrowserSessionsResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerDataRestoreData, PostBotsByBotIdContainerDataRestoreError, PostBotsByBotIdContainerDataRestoreErrors, PostBotsByBotIdContainerDataRestoreResponse, PostBotsByBotIdContainerDataRestoreResponses, PostBotsByBotIdContainerDisplayPrepareData, PostBotsByBotIdContainerDisplayPrepareError, PostBotsByBotIdContainerDisplayPrepareErrors, PostBotsByBotIdContainerDisplayPrepareResponse, PostBotsByBotIdContainerDisplayPrepareResponses, PostBotsByBotIdContainerDisplayWebrtcOfferData, PostBotsByBotIdContainerDisplayWebrtcOfferError, PostBotsByBotIdContainerDisplayWebrtcOfferErrors, PostBotsByBotIdContainerDisplayWebrtcOfferResponse, PostBotsByBotIdContainerDisplayWebrtcOfferResponses, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsArchiveData, PostBotsByBotIdContainerFsArchiveError, PostBotsByBotIdContainerFsArchiveErrors, PostBotsByBotIdContainerFsArchiveResponses, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsExtractData, PostBotsByBotIdContainerFsExtractError, PostBotsByBotIdContainerFsExtractErrors, PostBotsByBotIdContainerFsExtractResponse, PostBotsByBotIdContainerFsExtractResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsActionsData, PostBotsByBotIdContainerSkillsActionsError, PostBotsByBotIdContainerSkillsActionsErrors, PostBotsByBotIdContainerSkillsActionsResponse, PostBotsByBotIdContainerSkillsActionsResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerSnapshotsRollbackData, PostBotsByBotIdContainerSnapshotsRollbackError, PostBotsByBotIdContainerSnapshotsRollbackErrors, PostBotsByBotIdContainerSnapshotsRollbackResponse, PostBotsByBotIdContainerSnapshotsRollbackResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdHooksTestData, PostBotsByBotIdHooksTestError, PostBotsByBotIdHooksTestErrors, PostBotsByBotIdHooksTestResponse, PostBotsByBotIdHooksTestResponses, PostBotsByBotIdLocalMessagesData, PostBotsByBotIdLocalMessagesError, PostBotsByBotIdLocalMessagesErrors, PostBotsByBotIdLocalMessagesResponse, PostBotsByBotIdLocalMessagesResponses, PostBotsByBotIdMcpByIdOauthAuthorizeData, PostBotsByBotIdMcpByIdOauthAuthorizeError, PostBotsByBotIdMcpByIdOauthAuthorizeErrors, PostBotsByBotIdMcpByIdOauthAuthorizeResponse, PostBotsByBotIdMcpByIdOauthAuthorizeResponses, PostBotsByBotIdMcpByIdOauthDiscoverData, PostBotsByBotIdMcpByIdOauthDiscoverError, PostBotsByBotIdMcpByIdOauthDiscoverErrors, PostBotsByBotIdMcpByIdOauthDiscoverResponse, PostBotsByBotIdMcpByIdOauthDiscoverResponses, PostBotsByBotIdMcpByIdOauthExchangeData, PostBotsByBotIdMcpByIdOauthExchangeError, PostBotsByBotIdMcpByIdOauthExchangeErrors, PostBotsByBotIdMcpByIdOauthExchangeResponse, PostBotsByBotIdMcpByIdOauthExchangeResponses, PostBotsByBotIdMcpByIdProbeData, PostBotsByBotIdMcpByIdProbeError, PostBotsByBotIdMcpByIdProbeErrors, PostBotsByBotIdMcpByIdProbeResponse, PostBotsByBotIdMcpByIdProbeResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdPluginsByIdDisableData, PostBotsByBotIdPluginsByIdDisableError, PostBotsByBotIdPluginsByIdDisableErrors, PostBotsByBotIdPluginsByIdDisableResponse, PostBotsByBotIdPluginsByIdDisableResponses, PostBotsByBotIdPluginsByIdEnableData, PostBotsByBotIdPluginsByIdEnableError, PostBotsByBotIdPluginsByIdEnableErrors, PostBotsByBotIdPluginsByIdEnableResponse, PostBotsByBotIdPluginsByIdEnableResponses, PostBotsByBotIdPluginsByIdOauthAuthorizeData, PostBotsByBotIdPluginsByIdOauthAuthorizeError, PostBotsByBotIdPluginsByIdOauthAuthorizeErrors, PostBotsByBotIdPluginsByIdOauthAuthorizeResponse, PostBotsByBotIdPluginsByIdOauthAuthorizeResponses, PostBotsByBotIdPluginsByIdUninstallData, PostBotsByBotIdPluginsByIdUninstallError, PostBotsByBotIdPluginsByIdUninstallErrors, PostBotsByBotIdPluginsByIdUninstallResponse, PostBotsByBotIdPluginsByIdUninstallResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSessionsBySessionIdAcpRuntimeData, PostBotsByBotIdSessionsBySessionIdAcpRuntimeError, PostBotsByBotIdSessionsBySessionIdAcpRuntimeErrors, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponse, PostBotsByBotIdSessionsBySessionIdAcpRuntimeResponses, PostBotsByBotIdSessionsBySessionIdCompactData, PostBotsByBotIdSessionsBySessionIdCompactError, PostBotsByBotIdSessionsBySessionIdCompactErrors, PostBotsByBotIdSessionsBySessionIdCompactResponse, PostBotsByBotIdSessionsBySessionIdCompactResponses, PostBotsByBotIdSessionsData, PostBotsByBotIdSessionsError, PostBotsByBotIdSessionsErrors, PostBotsByBotIdSessionsResponse, PostBotsByBotIdSessionsResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSupermarketInstallPluginData, PostBotsByBotIdSupermarketInstallPluginError, PostBotsByBotIdSupermarketInstallPluginErrors, PostBotsByBotIdSupermarketInstallPluginResponse, PostBotsByBotIdSupermarketInstallPluginResponses, PostBotsByBotIdSupermarketInstallSkillData, PostBotsByBotIdSupermarketInstallSkillError, PostBotsByBotIdSupermarketInstallSkillErrors, PostBotsByBotIdSupermarketInstallSkillResponse, PostBotsByBotIdSupermarketInstallSkillResponses, PostBotsByBotIdToolApprovalsByApprovalIdApproveData, PostBotsByBotIdToolApprovalsByApprovalIdApproveError, PostBotsByBotIdToolApprovalsByApprovalIdApproveErrors, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponse, PostBotsByBotIdToolApprovalsByApprovalIdApproveResponses, PostBotsByBotIdToolApprovalsByApprovalIdRejectData, PostBotsByBotIdToolApprovalsByApprovalIdRejectError, PostBotsByBotIdToolApprovalsByApprovalIdRejectErrors, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponse, PostBotsByBotIdToolApprovalsByApprovalIdRejectResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdTtsSynthesizeData, PostBotsByBotIdTtsSynthesizeError, PostBotsByBotIdTtsSynthesizeErrors, PostBotsByBotIdTtsSynthesizeResponse, PostBotsByBotIdTtsSynthesizeResponses, PostBotsByBotIdUserAccessData, PostBotsByBotIdUserAccessError, PostBotsByBotIdUserAccessErrors, PostBotsByBotIdUserAccessResponse, PostBotsByBotIdUserAccessResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponse, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersErrors, PostEmailProvidersResponse, PostEmailProvidersResponses, PostFetchProvidersData, PostFetchProvidersError, PostFetchProvidersErrors, PostFetchProvidersResponse, PostFetchProvidersResponses, PostMemoryProvidersData, PostMemoryProvidersError, PostMemoryProvidersErrors, PostMemoryProvidersResponse, PostMemoryProvidersResponses, PostModelsByIdTestData, PostModelsByIdTestError, PostModelsByIdTestErrors, PostModelsByIdTestResponse, PostModelsByIdTestResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdImportModelsData, PostProvidersByIdImportModelsError, PostProvidersByIdImportModelsErrors, PostProvidersByIdImportModelsResponse, PostProvidersByIdImportModelsResponses, PostProvidersByIdOauthPollData, PostProvidersByIdOauthPollError, PostProvidersByIdOauthPollErrors, PostProvidersByIdOauthPollResponse, PostProvidersByIdOauthPollResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostSpeechModelsByIdTestData, PostSpeechModelsByIdTestError, PostSpeechModelsByIdTestErrors, PostSpeechModelsByIdTestResponses, PostSpeechProvidersByIdImportModelsData, PostSpeechProvidersByIdImportModelsError, PostSpeechProvidersByIdImportModelsErrors, PostSpeechProvidersByIdImportModelsResponse, PostSpeechProvidersByIdImportModelsResponses, PostTranscriptionModelsByIdTestData, PostTranscriptionModelsByIdTestError, PostTranscriptionModelsByIdTestErrors, PostTranscriptionModelsByIdTestResponse, PostTranscriptionModelsByIdTestResponses, PostTranscriptionProvidersByIdImportModelsData, PostTranscriptionProvidersByIdImportModelsError, PostTranscriptionProvidersByIdImportModelsErrors, PostTranscriptionProvidersByIdImportModelsResponse, PostTranscriptionProvidersByIdImportModelsResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersMeChannelLinksData, PostUsersMeChannelLinksError, PostUsersMeChannelLinksErrors, PostUsersMeChannelLinksResponse, PostUsersMeChannelLinksResponses, PostUsersResponse, PostUsersResponses, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersImportModelsResponse, ProvidersOAuthAccount, ProvidersOAuthAuthorizeResponse, ProvidersOAuthDeviceStatus, ProvidersOAuthStatus, ProvidersTestResponse, ProvidersTestStatus, ProvidersUpdateRequest, PutBotsByBotIdAclDefaultEffectData, PutBotsByBotIdAclDefaultEffectError, PutBotsByBotIdAclDefaultEffectErrors, PutBotsByBotIdAclDefaultEffectResponses, PutBotsByBotIdAclRulesByRuleIdData, PutBotsByBotIdAclRulesByRuleIdError, PutBotsByBotIdAclRulesByRuleIdErrors, PutBotsByBotIdAclRulesByRuleIdResponse, PutBotsByBotIdAclRulesByRuleIdResponses, PutBotsByBotIdContainerMetricsData, PutBotsByBotIdContainerMetricsError, PutBotsByBotIdContainerMetricsErrors, PutBotsByBotIdContainerMetricsResponse, PutBotsByBotIdContainerMetricsResponses, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdUserAccessByGrantIdData, PutBotsByBotIdUserAccessByGrantIdError, PutBotsByBotIdUserAccessByGrantIdErrors, PutBotsByBotIdUserAccessByGrantIdResponse, PutBotsByBotIdUserAccessByGrantIdResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponse, PutEmailProvidersByIdResponses, PutFetchProvidersByIdData, PutFetchProvidersByIdError, PutFetchProvidersByIdErrors, PutFetchProvidersByIdResponse, PutFetchProvidersByIdResponses, PutMemoryProvidersByIdData, PutMemoryProvidersByIdError, PutMemoryProvidersByIdErrors, PutMemoryProvidersByIdResponse, PutMemoryProvidersByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutSpeechModelsByIdData, PutSpeechModelsByIdError, PutSpeechModelsByIdErrors, PutSpeechModelsByIdResponse, PutSpeechModelsByIdResponses, PutTranscriptionModelsByIdData, PutTranscriptionModelsByIdError, PutTranscriptionModelsByIdErrors, PutTranscriptionModelsByIdResponse, PutTranscriptionModelsByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListLogsResponse, ScheduleListResponse, ScheduleLog, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SessionSession, SettingsSettings, SettingsToolApprovalConfig, SettingsToolApprovalExecPolicy, SettingsToolApprovalFilePolicy, SettingsUpsertRequest } from './types.gen'; diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts index ec8660d019..d71e414aa1 100644 --- a/packages/sdk/src/types.gen.ts +++ b/packages/sdk/src/types.gen.ts @@ -187,17 +187,6 @@ export type AcpprofilePublicProfile = { supported_backends?: Array; }; -export type AdaptersCdfPoint = { - /** - * cumulative weight fraction [0.0, 1.0] - */ - cumulative?: number; - /** - * rank position (1-based, sorted by value desc) - */ - k?: number; -}; - export type AdaptersCompactResult = { after_count?: number; before_count?: number; @@ -216,7 +205,6 @@ export type AdaptersHealthStatus = { export type AdaptersMemoryCompactCapability = { archive?: boolean; - native?: boolean; reason?: string; rebuild_index?: boolean; semantic?: boolean; @@ -225,7 +213,6 @@ export type AdaptersMemoryCompactCapability = { export type AdaptersMemoryItem = { agent_id?: string; bot_id?: string; - cdf_curve?: Array; created_at?: string; hash?: string; id?: string; @@ -235,7 +222,6 @@ export type AdaptersMemoryItem = { }; run_id?: string; score?: number; - top_k_buckets?: Array; updated_at?: string; }; @@ -335,17 +321,6 @@ export type AdaptersSearchResponse = { results?: Array; }; -export type AdaptersTopKBucket = { - /** - * sparse dimension index (term hash) - */ - index?: number; - /** - * weight (term frequency) - */ - value?: number; -}; - export type AdaptersUsageResponse = { avg_text_bytes?: number; count?: number; diff --git a/spec/docs.go b/spec/docs.go index 5aecb2381f..1f31933711 100644 --- a/spec/docs.go +++ b/spec/docs.go @@ -12513,19 +12513,6 @@ const docTemplate = `{ } } }, - "adapters.CDFPoint": { - "type": "object", - "properties": { - "cumulative": { - "description": "cumulative weight fraction [0.0, 1.0]", - "type": "number" - }, - "k": { - "description": "rank position (1-based, sorted by value desc)", - "type": "integer" - } - } - }, "adapters.CompactResult": { "type": "object", "properties": { @@ -12571,9 +12558,6 @@ const docTemplate = `{ "archive": { "type": "boolean" }, - "native": { - "type": "boolean" - }, "reason": { "type": "string" }, @@ -12594,12 +12578,6 @@ const docTemplate = `{ "bot_id": { "type": "string" }, - "cdf_curve": { - "type": "array", - "items": { - "$ref": "#/definitions/adapters.CDFPoint" - } - }, "created_at": { "type": "string" }, @@ -12622,12 +12600,6 @@ const docTemplate = `{ "score": { "type": "number" }, - "top_k_buckets": { - "type": "array", - "items": { - "$ref": "#/definitions/adapters.TopKBucket" - } - }, "updated_at": { "type": "string" } @@ -12867,19 +12839,6 @@ const docTemplate = `{ } } }, - "adapters.TopKBucket": { - "type": "object", - "properties": { - "index": { - "description": "sparse dimension index (term hash)", - "type": "integer" - }, - "value": { - "description": "weight (term frequency)", - "type": "number" - } - } - }, "adapters.UsageResponse": { "type": "object", "properties": { diff --git a/spec/swagger.json b/spec/swagger.json index ad0bf419d5..3dc8889d84 100644 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -12504,19 +12504,6 @@ } } }, - "adapters.CDFPoint": { - "type": "object", - "properties": { - "cumulative": { - "description": "cumulative weight fraction [0.0, 1.0]", - "type": "number" - }, - "k": { - "description": "rank position (1-based, sorted by value desc)", - "type": "integer" - } - } - }, "adapters.CompactResult": { "type": "object", "properties": { @@ -12562,9 +12549,6 @@ "archive": { "type": "boolean" }, - "native": { - "type": "boolean" - }, "reason": { "type": "string" }, @@ -12585,12 +12569,6 @@ "bot_id": { "type": "string" }, - "cdf_curve": { - "type": "array", - "items": { - "$ref": "#/definitions/adapters.CDFPoint" - } - }, "created_at": { "type": "string" }, @@ -12613,12 +12591,6 @@ "score": { "type": "number" }, - "top_k_buckets": { - "type": "array", - "items": { - "$ref": "#/definitions/adapters.TopKBucket" - } - }, "updated_at": { "type": "string" } @@ -12858,19 +12830,6 @@ } } }, - "adapters.TopKBucket": { - "type": "object", - "properties": { - "index": { - "description": "sparse dimension index (term hash)", - "type": "integer" - }, - "value": { - "description": "weight (term frequency)", - "type": "number" - } - } - }, "adapters.UsageResponse": { "type": "object", "properties": { diff --git a/spec/swagger.yaml b/spec/swagger.yaml index d1a6f4ea8e..b69c68d382 100644 --- a/spec/swagger.yaml +++ b/spec/swagger.yaml @@ -308,15 +308,6 @@ definitions: type: string type: array type: object - adapters.CDFPoint: - properties: - cumulative: - description: cumulative weight fraction [0.0, 1.0] - type: number - k: - description: rank position (1-based, sorted by value desc) - type: integer - type: object adapters.CompactResult: properties: after_count: @@ -346,8 +337,6 @@ definitions: properties: archive: type: boolean - native: - type: boolean reason: type: string rebuild_index: @@ -361,10 +350,6 @@ definitions: type: string bot_id: type: string - cdf_curve: - items: - $ref: '#/definitions/adapters.CDFPoint' - type: array created_at: type: string hash: @@ -380,10 +365,6 @@ definitions: type: string score: type: number - top_k_buckets: - items: - $ref: '#/definitions/adapters.TopKBucket' - type: array updated_at: type: string type: object @@ -542,15 +523,6 @@ definitions: $ref: '#/definitions/adapters.MemoryItem' type: array type: object - adapters.TopKBucket: - properties: - index: - description: sparse dimension index (term hash) - type: integer - value: - description: weight (term frequency) - type: number - type: object adapters.UsageResponse: properties: avg_text_bytes: