Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand All @@ -79,25 +75,21 @@ jobs:
strategy:
fail-fast: false
matrix:
image: [server, web, sparse, workspace]
image: [server, web, workspace]
platform: [linux/amd64, linux/arm64]
variant: ["", debian]
exclude:
- image: server
variant: debian
- image: web
variant: debian
- image: sparse
variant: debian
- image: workspace
variant: ""
include:
- image: server
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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 1 addition & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
11 changes: 5 additions & 6 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 0 additions & 8 deletions apps/web/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": {
Expand Down Expand Up @@ -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",
Expand All @@ -2020,7 +2013,6 @@
"memorySourceEntries": "Source Entries",
"memoryIndexedEntries": "Indexed Entries",
"memoryQdrantCollection": "Qdrant Collection",
"memoryEncoderHealth": "Sparse Encoder",
"memoryDenseEmbeddingHealth": "Embedding Backend",
"memoryQdrantHealth": "Qdrant",
"memoryHealthOk": "Healthy",
Expand Down
7 changes: 0 additions & 7 deletions apps/web/src/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "コレクションが存在します",
Expand All @@ -959,12 +956,10 @@
"collectionUnavailable": "利用不可",
"modeNames": {
"off": "オフ",
"sparse": "キーワード",
"dense": "セマンティック"
},
"modeDescriptions": {
"off": "長期記憶インデックスを作成せず、基本的なファイルメモリのみを保持します。",
"sparse": "オプションのスパース検索サービスを使用して、キーワードで記憶を照合します。",
"dense": "埋め込みモデルを使用して記憶をベクトルに変換し、意味の類似性で検索します。"
},
"providerNames": {
Expand Down Expand Up @@ -1985,8 +1980,6 @@
"memoryProvider": "MemoryProvider",
"memoryProviderPlaceholder": "MemoryProviderの選択 (空の場合は無効)",
"memoryModePreview": "選択された組み込みモード: {mode}",
"sparseStatusTitle": "スパース取得ステータス",
"sparseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から Qdrant インデックスが再構築されます。",
"denseStatusTitle": "密集検索ステータス",
"denseStatusHint": "Markdown ファイルは信頼できる情報源です。同期すると、/data/memory から高密度 Qdrant インデックスが再構築されます。",
"mem0StatusTitle": "Mem0 同期ステータス",
Expand Down
8 changes: 0 additions & 8 deletions apps/web/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 已存在",
Expand All @@ -1012,12 +1009,10 @@
"collectionUnavailable": "不可用",
"modeNames": {
"off": "关闭",
"sparse": "关键词",
"dense": "语义"
},
"modeDescriptions": {
"off": "不建立长期记忆索引,仅保留基础的文件记忆。",
"sparse": "用关键词匹配检索记忆,依赖可选的稀疏检索服务。",
"dense": "用向量模型把记忆转成向量,按语义相近度检索。"
},
"providerNames": {
Expand Down Expand Up @@ -2004,8 +1999,6 @@
"memoryProvider": "记忆提供方",
"memoryProviderPlaceholder": "选择记忆提供方(为空则禁用)",
"memoryModePreview": "当前内置模式:{mode}",
"sparseStatusTitle": "稀疏检索状态",
"sparseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重新构建 Qdrant 索引。",
"denseStatusTitle": "稠密检索状态",
"denseStatusHint": "Markdown 文件是唯一可信源;手动同步将根据 /data/memory 重建稠密 Qdrant 索引。",
"mem0StatusTitle": "Mem0 同步状态",
Expand All @@ -2020,7 +2013,6 @@
"memorySourceEntries": "源条目数",
"memoryIndexedEntries": "索引条目数",
"memoryQdrantCollection": "Qdrant Collection",
"memoryEncoderHealth": "Sparse Encoder",
"memoryDenseEmbeddingHealth": "Embedding 后端",
"memoryQdrantHealth": "Qdrant",
"memoryHealthOk": "正常",
Expand Down
16 changes: 6 additions & 10 deletions apps/web/src/pages/bots/components/settings-context-card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) {
Expand Down
12 changes: 2 additions & 10 deletions apps/web/src/pages/memory/components/builtin-config.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@
{{ $t(`memory.modeDescriptions.${mode}`) }}
</p>

<div
v-if="mode === 'sparse'"
class="rounded-lg border border-border bg-card px-3 py-2 text-xs text-muted-foreground"
>
{{ $t('memory.sparseInstallHint') }}
</div>

<div
v-if="mode === 'dense'"
class="space-y-3 rounded-lg border border-border bg-card p-4"
Expand Down Expand Up @@ -108,7 +101,7 @@ import { useI18n } from 'vue-i18n'
import LoadingButton from '@/components/loading-button/index.vue'
import ModelSelect from '@/pages/bots/components/model-select.vue'

type MemoryMode = 'off' | 'sparse' | 'dense'
type MemoryMode = 'off' | 'dense'

const props = defineProps<{
provider?: AdaptersProviderGetResponse | null
Expand Down Expand Up @@ -152,14 +145,13 @@ const collections = computed(() => (statusData.value as AdaptersProviderStatusRe

const modeItems = computed<SegmentedItem<MemoryMode>[]>(() => [
{ 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<string, unknown>
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 })

Expand Down
3 changes: 0 additions & 3 deletions conf/app.apple.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
3 changes: 0 additions & 3 deletions conf/app.docker.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
3 changes: 0 additions & 3 deletions conf/app.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
3 changes: 0 additions & 3 deletions conf/app.kata.docker.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
3 changes: 0 additions & 3 deletions conf/app.local.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Loading
Loading