Require local setup embedding profile selection

This commit is contained in:
“BeeRad”
2026-05-02 11:22:18 +10:00
parent 9575815d83
commit 16d222e3b4
4 changed files with 126 additions and 7 deletions
+10 -4
View File
@@ -6,13 +6,19 @@
# Get one at: https://platform.openai.com/api-keys # Get one at: https://platform.openai.com/api-keys
OPENAI_API_KEY= OPENAI_API_KEY=
# AI profiles. Defaults keep RA-H on OpenAI plus sqlite-vec. # AI profiles are selected during setup because embedding dimensions shape
LLM_PROFILE=openai # the sqlite-vec tables. Use one:
#
# npm run setup:local -- --profile openai
# npm run setup:local -- --profile qwen-local
#
# OpenAI profile:
# LLM_PROFILE=openai
# LLM_MODEL=gpt-4o-mini # LLM_MODEL=gpt-4o-mini
EMBEDDING_PROFILE=openai # EMBEDDING_PROFILE=openai
# EMBEDDING_MODEL=text-embedding-3-small # EMBEDDING_MODEL=text-embedding-3-small
# EMBEDDING_DIMENSIONS=1536 # EMBEDDING_DIMENSIONS=1536
VECTOR_BACKEND=sqlite-vec # VECTOR_BACKEND=sqlite-vec
# Supported local profile: point RA-H at OpenAI-compatible local endpoints. # Supported local profile: point RA-H at OpenAI-compatible local endpoints.
# Example Ollama: # Example Ollama:
+39 -2
View File
@@ -130,6 +130,43 @@ Full install details:
--- ---
## First-Time Setup
Pick the embedding profile before the database is created. This matters because
the readable `nodes` and `chunks` tables are normal SQLite tables, but the
derived sqlite-vec vector tables are created with a fixed embedding width.
OpenAI:
```bash
npm install
npm run setup:local -- --profile openai
npm run dev
```
Local Qwen with Ollama:
```bash
npm install
ollama pull qwen3:4b
ollama pull qwen3-embedding:0.6b
npm run setup:local -- --profile qwen-local
npm run dev
```
If you run setup without a profile and `.env.local` does not already select one,
setup stops before creating vector tables and prints the two supported commands.
If you change embedding provider, model, dimensions, or vector backend after
data exists, your source data stays intact but derived embeddings must be
rebuilt:
```bash
npm run rebuild:embeddings
```
---
## OpenAI API Key ## OpenAI API Key
**Optional but recommended.** Without a key, you can still create and organize nodes manually. **Optional but recommended.** Without a key, you can still create and organize nodes manually.
@@ -149,7 +186,7 @@ Get a key at [platform.openai.com/api-keys](https://platform.openai.com/api-keys
## Local Model Profile ## Local Model Profile
OpenAI remains the default supported path. If you want local utility LLM calls and local embeddings, run a local OpenAI-compatible model server and point RA-H at it. OpenAI remains the default supported cloud path. If you want local utility LLM calls and local embeddings, run a local OpenAI-compatible model server and point RA-H at it.
Supported local contract: Supported local contract:
@@ -228,7 +265,7 @@ If you need a clean demo without touching your normal RA-H database:
git clone https://github.com/bradwmorris/ra-h_os.git ~/Desktop/ra-h_os-demo git clone https://github.com/bradwmorris/ra-h_os.git ~/Desktop/ra-h_os-demo
cd ~/Desktop/ra-h_os-demo cd ~/Desktop/ra-h_os-demo
npm install npm install
SQLITE_DB_PATH="$HOME/Desktop/ra-h_os-demo-data/rah.sqlite" npm run setup:local SQLITE_DB_PATH="$HOME/Desktop/ra-h_os-demo-data/rah.sqlite" npm run setup:local -- --profile qwen-local
npm run dev npm run dev
npx -y ra-h-mcp-server@latest setup \ npx -y ra-h-mcp-server@latest setup \
+76
View File
@@ -8,6 +8,7 @@ import Database from 'better-sqlite3';
const repoDir = process.cwd(); const repoDir = process.cwd();
const envTemplate = path.join(repoDir, '.env.example'); const envTemplate = path.join(repoDir, '.env.example');
const targetEnv = path.join(repoDir, '.env.local'); const targetEnv = path.join(repoDir, '.env.local');
const supportedProfiles = new Set(['openai', 'qwen-local']);
function log(message) { function log(message) {
console.log(`[bootstrap-local] ${message}`); console.log(`[bootstrap-local] ${message}`);
@@ -89,10 +90,12 @@ function ensureEnvValue(key, value) {
const nextLines = lines.map((line) => { const nextLines = lines.map((line) => {
const trimmed = line.trim(); const trimmed = line.trim();
if (trimmed.startsWith(`${key}=`)) { if (trimmed.startsWith(`${key}=`)) {
if (found) return line;
found = true; found = true;
return `${key}=${value}`; return `${key}=${value}`;
} }
if (trimmed.startsWith(`# ${key}=`) || trimmed.startsWith(`#${key}=`)) { if (trimmed.startsWith(`# ${key}=`) || trimmed.startsWith(`#${key}=`)) {
if (found) return line;
found = true; found = true;
return `${key}=${value}`; return `${key}=${value}`;
} }
@@ -110,6 +113,74 @@ function ensureEnvValue(key, value) {
log(`Set ${key} in .env.local`); log(`Set ${key} in .env.local`);
} }
function parseArgs(argv) {
const args = { profile: undefined };
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
if (arg === '--profile') {
args.profile = argv[index + 1];
index += 1;
} else if (arg.startsWith('--profile=')) {
args.profile = arg.slice('--profile='.length);
}
}
return args;
}
function normalizeSetupProfile(rawProfile) {
if (!rawProfile) return undefined;
if (rawProfile === 'qwen' || rawProfile === 'local' || rawProfile === 'ollama') {
return 'qwen-local';
}
return rawProfile;
}
function applySetupProfile(profile) {
if (!profile) return;
if (!supportedProfiles.has(profile)) {
throw new Error(`Unsupported setup profile "${profile}". Use "openai" or "qwen-local".`);
}
if (profile === 'openai') {
ensureEnvValue('LLM_PROFILE', 'openai');
ensureEnvValue('EMBEDDING_PROFILE', 'openai');
ensureEnvValue('EMBEDDING_MODEL', 'text-embedding-3-small');
ensureEnvValue('EMBEDDING_DIMENSIONS', '1536');
ensureEnvValue('VECTOR_BACKEND', 'sqlite-vec');
return;
}
ensureEnvValue('LLM_PROFILE', 'openai-compatible');
ensureEnvValue('LLM_BASE_URL', 'http://127.0.0.1:11434/v1');
ensureEnvValue('LLM_MODEL', 'qwen3:4b');
ensureEnvValue('EMBEDDING_PROFILE', 'openai-compatible');
ensureEnvValue('EMBEDDING_BASE_URL', 'http://127.0.0.1:11434/v1');
ensureEnvValue('EMBEDDING_MODEL', 'qwen3-embedding:0.6b');
ensureEnvValue('EMBEDDING_DIMENSIONS', '1024');
ensureEnvValue('VECTOR_BACKEND', 'sqlite-vec');
}
function assertEmbeddingProfileSelected(env) {
if (env.EMBEDDING_PROFILE) {
return;
}
throw new Error([
'Choose an embedding profile before database setup.',
'',
'The selected embedding model controls sqlite-vec table dimensions:',
' OpenAI text-embedding-3-small -> 1536',
' Local Qwen qwen3-embedding:0.6b -> 1024',
'',
'Run one of:',
' npm run setup:local -- --profile openai',
' npm run setup:local -- --profile qwen-local',
'',
'If you change embedding provider later, run:',
' npm run rebuild:embeddings',
].join('\n'));
}
function ensureCoreSchema(db) { function ensureCoreSchema(db) {
db.pragma('foreign_keys = ON'); db.pragma('foreign_keys = ON');
db.exec(` db.exec(`
@@ -590,9 +661,14 @@ function main() {
throw new Error(`Node.js 20+ required (found ${process.version})`); throw new Error(`Node.js 20+ required (found ${process.version})`);
} }
const args = parseArgs(process.argv.slice(2));
const setupProfile = normalizeSetupProfile(args.profile);
ensureEnvFile(); ensureEnvFile();
applySetupProfile(setupProfile);
const env = { ...parseEnvFile(targetEnv), ...process.env }; const env = { ...parseEnvFile(targetEnv), ...process.env };
assertEmbeddingProfileSelected(env);
if (process.env.SQLITE_DB_PATH) { if (process.env.SQLITE_DB_PATH) {
ensureEnvValue('SQLITE_DB_PATH', process.env.SQLITE_DB_PATH); ensureEnvValue('SQLITE_DB_PATH', process.env.SQLITE_DB_PATH);
env.SQLITE_DB_PATH = process.env.SQLITE_DB_PATH; env.SQLITE_DB_PATH = process.env.SQLITE_DB_PATH;
+1 -1
View File
@@ -23,7 +23,7 @@ console.log('[setup-local] Rebuilding better-sqlite3 native bindings');
run('npm', ['rebuild', 'better-sqlite3']); run('npm', ['rebuild', 'better-sqlite3']);
console.log('[setup-local] Bootstrapping local RA-H database'); console.log('[setup-local] Bootstrapping local RA-H database');
run('npm', ['run', 'bootstrap:local']); run('npm', ['run', 'bootstrap:local', '--', ...process.argv.slice(2)]);
console.log(''); console.log('');
console.log('[setup-local] Local app setup complete.'); console.log('[setup-local] Local app setup complete.');