Skip to content
Merged
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
9 changes: 5 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
# These are the ONLY variables you need to start PowerMem. Everything else has
# a safe default, so the system runs out of the box:
#
# - Database -> OceanBase provider with no host configured, which boots
# embedded seekdb on disk at ./seekdb_data (same engine,
# same SQL surface, no separate server). Set OCEANBASE_HOST
# in `.env.example.full` to point at a remote cluster.
# - Database -> platform-aware default. Linux with embedded SeekDB available
# uses OceanBase/SeekDB on disk; other zero-config platforms
# use SQLite at ./data/powermem_dev.db for basic CRUD/search.
# Use OceanBase/SeekDB for Graph Store, sub_stores, sparse
# vectors, and SkillStore.
# - Embedder -> built-in local all-MiniLM-L6-v2 (no API key required;
# model auto-downloads to ~/.cache on first use)
# - Reranker / graph store / telemetry / audit -> sensible defaults / off
Expand Down
17 changes: 9 additions & 8 deletions .env.example.full
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
# cp .env.example.full .env
#
# Notes on defaults:
# - Database: DATABASE_PROVIDER=oceanbase with OCEANBASE_HOST left empty,
# which boots embedded seekdb on disk (same engine, no separate server).
# Set OCEANBASE_HOST to point at a real OceanBase cluster instead.
# - Database: platform-aware. Linux with embedded SeekDB available defaults
# to OceanBase/SeekDB on disk; other zero-config platforms default to
# SQLite for basic local memory CRUD/search. Use OceanBase/SeekDB for the
# full feature stack.
# - Embedder: built-in `all-MiniLM-L6-v2` (384 dims) running locally with no
# API key. Setting EMBEDDING_PROVIDER below switches to a cloud / self-
# hosted embedder instead.
Expand All @@ -36,14 +37,14 @@
# =============================================================================
#
# DATABASE_PROVIDER — which storage engine PowerMem talks to.
# Recommended: oceanbase (the OceanBase backend covers both deployment
# shapes: leave OCEANBASE_HOST empty for embedded
# seekdb on disk — zero ops, no separate server —
# Recommended: oceanbase (full capability stack; leave OCEANBASE_HOST empty
# for embedded SeekDB on supported Linux installs,
# or set OCEANBASE_HOST to point at a remote
# OceanBase cluster)
# Other options: sqlite (smallest footprint, dev-only),
# Leave unset to use the platform-aware default.
# Other options: sqlite (smallest footprint, basic local CRUD/search),
# postgres / pgvector (if your stack already runs PostgreSQL)
DATABASE_PROVIDER=oceanbase
# DATABASE_PROVIDER=oceanbase

# -----------------------------------------------------------------------------
# OceanBase — used when DATABASE_PROVIDER=oceanbase. The same provider covers
Expand Down
9 changes: 6 additions & 3 deletions apps/claude-code-plugin/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,9 @@ writing. Never silently patch `.env`.**
with LLM_PROVIDER / LLM_API_KEY or LLM_AUTH_TOKEN / LLM_MODEL set to real
values (not placeholders
like `your_api_key_here`), REUSE it — skip directly to step 3a/3b. Only collect
what is missing. Use zero-config defaults for everything else (storage = embedded
seekdb, embedder = local all-MiniLM-L6-v2) unless I say otherwise.
what is missing. Use zero-config defaults for everything else (storage =
platform-aware: embedded SeekDB on supported Linux installs, otherwise SQLite
basic mode; embedder = local all-MiniLM-L6-v2) unless I say otherwise.

**2a. Auto-detect or manual?** Use AskUserQuestion (single-select):

Expand Down Expand Up @@ -359,7 +360,9 @@ writing. Never silently patch `.env`.**
```
⚠️ Both extras are required: `[server]` adds fastapi/uvicorn and
fastmcp for the HTTP API plus MCP transports;
`[seekdb]` adds the embedded seekdb storage backend (default).
`[seekdb]` adds the embedded seekdb storage backend. Platforms where
embedded SeekDB is unavailable default to SQLite basic mode; use
OceanBase/SeekDB for the full capability stack.
- Immediately after `uv pip install`, detect which Python interpreter was used. Read
the shebang from the freshly-installed `powermem-server` entry point — this is
the only reliable way to guarantee that the model-download script, the uv call
Expand Down
59 changes: 59 additions & 0 deletions apps/vscode-extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions dashboard/src/components/system-health-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ export function SystemHealthCard({ status }: SystemHealthCardProps) {

const systemStatus = getStatusDisplay(status.status);
const StatusIcon = systemStatus.icon;
const storageCapabilities = status.storage_capabilities;
const storageLimitations = storageCapabilities?.limitations ?? [];
const showStorageWarning =
status.storage_type?.toLowerCase() === "sqlite" ||
(storageCapabilities?.provider === "sqlite" &&
storageCapabilities.full_stack_available === false);

return (
<Card>
Expand Down Expand Up @@ -143,6 +149,25 @@ export function SystemHealthCard({ status }: SystemHealthCardProps) {
</div>
)}

{showStorageWarning && (
<div className="rounded-md border border-yellow-200 bg-yellow-50 px-3 py-2 text-sm text-yellow-900 dark:border-yellow-900/60 dark:bg-yellow-950/40 dark:text-yellow-100">
<div className="flex items-start gap-2">
<AlertCircle className="mt-0.5 size-4 shrink-0" />
<div className="space-y-1">
<p className="font-medium">
{t("dashboard.systemHealth.sqliteWarningTitle")}
</p>
<p>{t("dashboard.systemHealth.sqliteWarningDescription")}</p>
{storageLimitations.length > 0 && (
<p className="text-xs">
{storageLimitations.join("; ")}
</p>
)}
</div>
</div>
</div>
)}

{/* Dependencies Table */}
{status.dependencies &&
Object.keys(status.dependencies).length > 0 && (
Expand Down
4 changes: 3 additions & 1 deletion dashboard/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
"statusDown": "Down",
"latency": "latency",
"noStatus": "No status available",
"loading": "Loading system status..."
"loading": "Loading system status...",
"sqliteWarningTitle": "SQLite basic mode",
"sqliteWarningDescription": "PowerMem is running on SQLite. Basic memory CRUD and search are available, but the full capability stack requires OceanBase or embedded SeekDB."
},
"charts": {
"growthTrend": "Growth Trend",
Expand Down
4 changes: 3 additions & 1 deletion dashboard/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
"statusDown": "故障",
"latency": "延迟",
"noStatus": "暂无状态信息",
"loading": "加载系统状态中..."
"loading": "加载系统状态中...",
"sqliteWarningTitle": "SQLite 基础模式",
"sqliteWarningDescription": "PowerMem 当前使用 SQLite。基础记忆 CRUD 与搜索可用,完整能力栈需要切换到 OceanBase 或嵌入式 SeekDB。"
},
"charts": {
"growthTrend": "增长趋势",
Expand Down
9 changes: 9 additions & 0 deletions dashboard/src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export interface SystemStatus {
version: string;
storage_type?: string;
llm_provider?: string;
memory_service_ready?: boolean;
startup_error?: string;
storage_capabilities?: {
provider?: string;
defaulted: boolean;
full_stack_available: boolean;
limitations: string[];
recommendation?: string;
};
uptime_seconds: number;
started_at: string;
dependencies: Record<string, DependencyStatus>;
Expand Down
18 changes: 9 additions & 9 deletions docs/guides/0003-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,17 @@ memory = Memory(config=config)

## 1. Database Configuration (Required)

PowerMem requires a database provider to store memories and vectors. Choose one of the supported providers: SQLite (development), OceanBase (production), or PostgreSQL.
PowerMem requires a database provider to store memories and vectors. The zero-config default is platform-aware: Linux with embedded SeekDB available uses the OceanBase provider in embedded mode; other platforms fall back to SQLite for basic local memory CRUD/search. Choose OceanBase/SeekDB for the full capability stack, or PostgreSQL/pgvector when your deployment already runs PostgreSQL.

### Common Database Settings

| Configuration | Type | Required | Default | Description |
|--------------|------|----------|---------|-------------|
| `DATABASE_PROVIDER` | string | Yes | `sqlite` | Database provider to use. Options: `sqlite`, `oceanbase`, `postgres` |
| `DATABASE_PROVIDER` | string | No | platform-aware | Database provider to use. Options: `sqlite`, `oceanbase`, `postgres` |

### SQLite Configuration

SQLite is the default database provider, recommended for development and single-user applications.
SQLite is the fallback default when embedded SeekDB is unavailable. It is recommended for development and single-user basic memory CRUD/search. Graph Store, sub_stores, sparse vector search, and SkillStore require OceanBase or embedded SeekDB.

| Configuration | Type | Required | Default | Description |
|--------------|------|----------|---------|-------------|
Expand Down Expand Up @@ -163,15 +163,15 @@ config = {

### OceanBase Configuration

OceanBase is recommended for production deployments and enterprise applications with high-scale requirements.
OceanBase is recommended for production deployments and for the full PowerMem capability stack. With `OCEANBASE_HOST` empty, the OceanBase provider uses embedded SeekDB when the native dependency is available. Set `OCEANBASE_HOST` for a remote OceanBase cluster.

| Configuration | Type | Required | Default | Description |
|--------------|------|----------|---------|-------------|
| `OCEANBASE_HOST` | string | Yes* | `127.0.0.1` | OceanBase server hostname or IP address. Required when `DATABASE_PROVIDER=oceanbase` |
| `OCEANBASE_PORT` | integer | Yes* | `2881` | OceanBase server port. Required when `DATABASE_PROVIDER=oceanbase` |
| `OCEANBASE_USER` | string | Yes* | `root` | Database username. Required when `DATABASE_PROVIDER=oceanbase` |
| `OCEANBASE_PASSWORD` | string | Yes* | - | Database password. Required when `DATABASE_PROVIDER=oceanbase` |
| `OCEANBASE_DATABASE` | string | Yes* | `powermem` | Database name. Required when `DATABASE_PROVIDER=oceanbase` |
| `OCEANBASE_HOST` | string | No* | empty | OceanBase server hostname or IP address. Empty means embedded SeekDB mode; remote mode requires a host |
| `OCEANBASE_PORT` | integer | Yes* | `2881` | OceanBase server port. Required in remote mode |
| `OCEANBASE_USER` | string | Yes* | `root@test` | Database username. Required in remote mode |
| `OCEANBASE_PASSWORD` | string | No | - | Database password. Required if your remote cluster needs one |
| `OCEANBASE_DATABASE` | string | Yes* | `test` | Database name |
| `OCEANBASE_COLLECTION` | string | No | `memories` | Collection/table name for storing memories |
| `OCEANBASE_INDEX_TYPE` | string | No | `IVF_FLAT` | Vector index type. Options: `IVF_FLAT`, `HNSW`, etc. |
| `OCEANBASE_VECTOR_METRIC_TYPE` | string | No | `cosine` | Vector similarity metric. Options: `cosine`, `euclidean`, `dot_product` |
Expand Down
34 changes: 26 additions & 8 deletions src/powermem/cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
print_warning,
print_info,
)
from powermem.platform_defaults import (
database_provider_explicitly_configured,
default_database_provider,
embedded_seekdb_available,
embedded_seekdb_unavailable_message,
sqlite_capability_warning,
)

logger = logging.getLogger(__name__)
from ..utils.envfile import read_env_file, update_env_file
Expand Down Expand Up @@ -295,11 +302,22 @@ def _validate_loaded_config(config: Dict[str, Any], strict: bool) -> Dict[str, A
provider = vector_store_config.get("provider", "")
inner_config = vector_store_config.get("config", {})
if provider == "oceanbase":
required_fields = ["host", "port", "user", "db_name"]
conn_args = inner_config.get("connection_args", {})
for field in required_fields:
if not conn_args.get(field):
errors.append(f"OceanBase connection missing: {field}")
host = conn_args.get("host") or inner_config.get("host")
if host:
required_fields = ["port", "user", "db_name"]
for field in required_fields:
if not (conn_args.get(field) or inner_config.get(field)):
errors.append(f"OceanBase connection missing: {field}")
elif not embedded_seekdb_available():
errors.append(embedded_seekdb_unavailable_message())
elif provider == "sqlite":
warning = sqlite_capability_warning(
provider,
defaulted=not database_provider_explicitly_configured(),
)
if warning:
warnings.append(warning)
elif provider in ("postgres", "pgvector"):
required_fields = ["host", "port", "user", "dbname"]
for field in required_fields:
Expand Down Expand Up @@ -795,7 +813,7 @@ def _discover_env_example(start_dir: Path, max_parent_levels: int = 8) -> Option

def _wizard_database(existing: Dict[str, str]) -> Dict[str, str]:
updates: Dict[str, str] = {}
provider_default = existing.get("DATABASE_PROVIDER") or "sqlite"
provider_default = existing.get("DATABASE_PROVIDER") or default_database_provider()
provider = click.prompt(
"Database provider",
type=click.Choice(["oceanbase", "postgres", "sqlite"], case_sensitive=False),
Expand Down Expand Up @@ -909,7 +927,7 @@ def _wizard_database_quickstart(existing: Dict[str, str]) -> Dict[str, str]:
This intentionally avoids optional knobs (WAL/timeout/collection/etc.).
"""
updates: Dict[str, str] = {}
provider_default = existing.get("DATABASE_PROVIDER") or "sqlite"
provider_default = existing.get("DATABASE_PROVIDER") or default_database_provider()
provider = click.prompt(
"Database provider",
type=click.Choice(["sqlite", "oceanbase", "postgres"], case_sensitive=False),
Expand Down Expand Up @@ -1278,7 +1296,7 @@ def init_cmd(ctx: CLIContext, env_file: Optional[str], dry_run: bool, test: bool
emb_updates = _wizard_embedder_quickstart(existing, llm_updates=llm_updates)
updates.update(emb_updates)

db_provider = updates.get("DATABASE_PROVIDER") or existing.get("DATABASE_PROVIDER") or "sqlite"
db_provider = updates.get("DATABASE_PROVIDER") or existing.get("DATABASE_PROVIDER") or default_database_provider()
dims = emb_updates.get("EMBEDDING_DIMS")
if dims:
updates.update(_sync_vector_dims_quickstart(db_provider=db_provider, dims=dims))
Expand All @@ -1293,7 +1311,7 @@ def init_cmd(ctx: CLIContext, env_file: Optional[str], dry_run: bool, test: bool
emb_updates = _wizard_embedder(existing, llm_updates=llm_updates)
updates.update(emb_updates)

db_provider = updates.get("DATABASE_PROVIDER") or existing.get("DATABASE_PROVIDER") or "sqlite"
db_provider = updates.get("DATABASE_PROVIDER") or existing.get("DATABASE_PROVIDER") or default_database_provider()
dims = emb_updates.get("EMBEDDING_DIMS")
if dims:
updates.update(_maybe_sync_vector_dims(db_provider=db_provider, dims=dims))
Expand Down
Loading
Loading