Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -245,8 +245,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 @@ -351,7 +352,9 @@ writing. Never silently patch `.env`.**
⚠️ All three extras are required: `[server]` adds fastapi/uvicorn; `[mcp]` adds
fastmcp, which is checked at import time and calls sys.exit(1) if missing —
this kills the HTTP server before it can start even in HTTP-only mode;
`[seekdb]` adds the embedded seekdb storage backend (default).
`[seekdb]` adds the embedded seekdb storage backend. Without it, 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 @@ -47,7 +47,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 @@ -47,7 +47,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 @@ -147,15 +147,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
30 changes: 22 additions & 8 deletions src/powermem/cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
print_warning,
print_info,
)
from powermem.platform_defaults import (
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 +301,19 @@ 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 bool(os.environ.get("DATABASE_PROVIDER")))
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 +809,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 +923,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 +1292,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 +1307,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