555e282cc4
ci / changelog_check (push) Waiting to run
ci / check_changes (push) Waiting to run
ci / build_mem0 (3.10) (push) Blocked by required conditions
ci / build_mem0 (3.11) (push) Blocked by required conditions
ci / build_mem0 (3.12) (push) Blocked by required conditions
CLI Node CI / lint (push) Waiting to run
CLI Node CI / test (20) (push) Waiting to run
CLI Node CI / test (22) (push) Waiting to run
CLI Node CI / build (push) Waiting to run
CLI Python CI / lint (push) Waiting to run
CLI Python CI / test (3.10) (push) Waiting to run
CLI Python CI / test (3.11) (push) Waiting to run
CLI Python CI / test (3.12) (push) Waiting to run
CLI Python CI / build (push) Waiting to run
openclaw checks / lint (push) Waiting to run
openclaw checks / test (20) (push) Waiting to run
openclaw checks / test (22) (push) Waiting to run
openclaw checks / build (push) Waiting to run
opencode-plugin checks / build (push) Waiting to run
pi-agent-plugin checks / lint (push) Waiting to run
pi-agent-plugin checks / test (20) (push) Waiting to run
pi-agent-plugin checks / test (22) (push) Waiting to run
pi-agent-plugin checks / build (push) Waiting to run
TypeScript SDK CI / check_changes (push) Waiting to run
TypeScript SDK CI / changelog_check (push) Blocked by required conditions
TypeScript SDK CI / build_ts_sdk (20) (push) Blocked by required conditions
TypeScript SDK CI / build_ts_sdk (22) (push) Blocked by required conditions
TypeScript SDK CI / integration_ts_sdk (20) (push) Blocked by required conditions
TypeScript SDK CI / integration_ts_sdk (22) (push) Blocked by required conditions
240 lines
8.6 KiB
TypeScript
240 lines
8.6 KiB
TypeScript
/**
|
|
* OSS provider wizard — provider definitions, config builders, and validation.
|
|
*
|
|
* Used by the init command for both interactive wizard and non-interactive
|
|
* --oss-* flag paths.
|
|
*/
|
|
|
|
import { join } from "node:path";
|
|
import { homedir } from "node:os";
|
|
|
|
// ============================================================================
|
|
// Provider definitions
|
|
// ============================================================================
|
|
|
|
export interface ProviderDef {
|
|
id: string;
|
|
label: string;
|
|
needsApiKey: boolean;
|
|
needsUrl: boolean;
|
|
envVar?: string;
|
|
defaultModel: string;
|
|
defaultUrl?: string;
|
|
}
|
|
|
|
export const LLM_PROVIDERS: ProviderDef[] = [
|
|
{ id: "openai", label: "OpenAI (requires API key)", needsApiKey: true, needsUrl: false, envVar: "OPENAI_API_KEY", defaultModel: "gpt-5-mini" },
|
|
{ id: "ollama", label: "Ollama (local, no API key)", needsApiKey: false, needsUrl: true, defaultModel: "llama3.1:8b", defaultUrl: "http://localhost:11434" },
|
|
{ id: "anthropic", label: "Anthropic (requires API key)", needsApiKey: true, needsUrl: false, envVar: "ANTHROPIC_API_KEY", defaultModel: "claude-sonnet-4-5-20250514" },
|
|
];
|
|
|
|
export interface EmbedderDef extends ProviderDef {
|
|
defaultDims: number;
|
|
}
|
|
|
|
export const EMBEDDER_PROVIDERS: EmbedderDef[] = [
|
|
{ id: "openai", label: "OpenAI (requires API key)", needsApiKey: true, needsUrl: false, envVar: "OPENAI_API_KEY", defaultModel: "text-embedding-3-small", defaultDims: 1536 },
|
|
{ id: "ollama", label: "Ollama (local, no API key)", needsApiKey: false, needsUrl: true, defaultModel: "nomic-embed-text", defaultUrl: "http://localhost:11434", defaultDims: 768 },
|
|
];
|
|
|
|
export interface VectorDef {
|
|
id: string;
|
|
label: string;
|
|
needsConnection: boolean;
|
|
defaultUrl?: string;
|
|
defaultPort?: number;
|
|
setupHint?: string;
|
|
}
|
|
|
|
export const VECTOR_PROVIDERS: VectorDef[] = [
|
|
{ id: "qdrant", label: "Qdrant (requires server — Docker or cloud)", needsConnection: true, defaultUrl: "http://localhost:6333", defaultPort: 6333, setupHint: "docker run -d -p 6333:6333 qdrant/qdrant" },
|
|
{ id: "pgvector", label: "PGVector (requires PostgreSQL + pgvector extension)", needsConnection: true, defaultPort: 5432, setupHint: "docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg17" },
|
|
];
|
|
|
|
export const KNOWN_EMBEDDER_DIMS: Record<string, number> = {
|
|
"text-embedding-3-small": 1536,
|
|
"text-embedding-3-large": 3072,
|
|
"text-embedding-ada-002": 1536,
|
|
"nomic-embed-text": 768,
|
|
"mxbai-embed-large": 1024,
|
|
"all-minilm": 384,
|
|
"snowflake-arctic-embed": 1024,
|
|
};
|
|
|
|
export function collectionNameForDims(dims: number): string {
|
|
return `mem0_${dims}d`;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Config builders
|
|
// ============================================================================
|
|
|
|
export interface LlmConfigInput {
|
|
apiKey?: string;
|
|
model?: string;
|
|
url?: string;
|
|
}
|
|
|
|
export function buildOssLlmConfig(
|
|
providerId: string,
|
|
input: LlmConfigInput,
|
|
): { provider: string; config: Record<string, unknown> } {
|
|
const def = LLM_PROVIDERS.find((p) => p.id === providerId);
|
|
if (!def) throw new Error(`Unknown LLM provider: ${providerId}`);
|
|
|
|
const config: Record<string, unknown> = {
|
|
model: input.model || def.defaultModel,
|
|
};
|
|
if (input.apiKey) config.apiKey = input.apiKey;
|
|
if (providerId === "ollama") {
|
|
config.url = input.url || def.defaultUrl;
|
|
}
|
|
return { provider: providerId, config };
|
|
}
|
|
|
|
export interface EmbedderConfigInput {
|
|
apiKey?: string;
|
|
model?: string;
|
|
url?: string;
|
|
}
|
|
|
|
export function buildOssEmbedderConfig(
|
|
providerId: string,
|
|
input: EmbedderConfigInput,
|
|
): { provider: string; config: Record<string, unknown>; dims: number | undefined } {
|
|
const def = EMBEDDER_PROVIDERS.find((p) => p.id === providerId);
|
|
if (!def) throw new Error(`Unknown embedder provider: ${providerId}`);
|
|
|
|
const model = input.model || def.defaultModel;
|
|
const config: Record<string, unknown> = { model };
|
|
if (input.apiKey) config.apiKey = input.apiKey;
|
|
if (providerId === "ollama") {
|
|
config.url = input.url || def.defaultUrl;
|
|
}
|
|
|
|
const dims = KNOWN_EMBEDDER_DIMS[model] ?? def.defaultDims;
|
|
if (dims) config.embeddingDims = dims;
|
|
return { provider: providerId, config, dims };
|
|
}
|
|
|
|
export interface VectorConfigInput {
|
|
url?: string;
|
|
host?: string;
|
|
port?: string;
|
|
user?: string;
|
|
password?: string;
|
|
dbname?: string;
|
|
apiKey?: string;
|
|
dims?: number;
|
|
}
|
|
|
|
export function buildOssVectorConfig(
|
|
providerId: string,
|
|
input: VectorConfigInput,
|
|
): { provider: string; config: Record<string, unknown> } {
|
|
const config: Record<string, unknown> = {};
|
|
|
|
if (providerId === "qdrant") {
|
|
config.url = input.url || "http://localhost:6333";
|
|
config.onDisk = true;
|
|
if (input.apiKey) config.apiKey = input.apiKey;
|
|
} else if (providerId === "pgvector") {
|
|
config.host = input.host || "localhost";
|
|
config.port = parseInt(input.port || "5432", 10);
|
|
if (input.user) config.user = input.user;
|
|
if (input.password) config.password = input.password;
|
|
config.dbname = input.dbname || "postgres";
|
|
}
|
|
|
|
if (input.dims) {
|
|
config.dimension = input.dims;
|
|
config.embeddingModelDims = input.dims;
|
|
config.collectionName = collectionNameForDims(input.dims);
|
|
}
|
|
return { provider: providerId, config };
|
|
}
|
|
|
|
export async function checkOllamaConnectivity(url: string): Promise<{ ok: boolean; error?: string }> {
|
|
try {
|
|
const resp = await fetch(`${url.replace(/\/+$/, "")}/api/tags`, { signal: AbortSignal.timeout(3000) });
|
|
if (resp.ok) return { ok: true };
|
|
return { ok: false, error: `Ollama returned HTTP ${resp.status}` };
|
|
} catch {
|
|
return { ok: false, error: `Cannot reach Ollama at ${url}. Install: https://ollama.com/download` };
|
|
}
|
|
}
|
|
|
|
export async function checkPgConnectivity(host: string, port: number): Promise<{ ok: boolean; error?: string }> {
|
|
return new Promise((resolve) => {
|
|
import("node:net").then(({ createConnection }) => {
|
|
const sock = createConnection({ host, port, timeout: 3000 });
|
|
sock.once("connect", () => { sock.destroy(); resolve({ ok: true }); });
|
|
sock.once("timeout", () => { sock.destroy(); resolve({ ok: false, error: `PostgreSQL not reachable at ${host}:${port}` }); });
|
|
sock.once("error", () => { sock.destroy(); resolve({ ok: false, error: `PostgreSQL not reachable at ${host}:${port}. Ensure PostgreSQL with pgvector extension is running.` }); });
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function checkQdrantConnectivity(url: string): Promise<{ ok: boolean; error?: string }> {
|
|
try {
|
|
const resp = await fetch(`${url.replace(/\/+$/, "")}/healthz`, { signal: AbortSignal.timeout(3000) });
|
|
if (resp.ok) return { ok: true };
|
|
return { ok: false, error: `Qdrant returned HTTP ${resp.status}` };
|
|
} catch (err) {
|
|
return { ok: false, error: `Cannot reach Qdrant at ${url}. Start it with: docker run -d -p 6333:6333 qdrant/qdrant` };
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Non-interactive flag validation
|
|
// ============================================================================
|
|
|
|
export interface OssFlags {
|
|
ossLlm?: string;
|
|
ossLlmKey?: string;
|
|
ossLlmModel?: string;
|
|
ossLlmUrl?: string;
|
|
ossEmbedder?: string;
|
|
ossEmbedderKey?: string;
|
|
ossEmbedderModel?: string;
|
|
ossEmbedderUrl?: string;
|
|
ossVector?: string;
|
|
ossVectorUrl?: string;
|
|
ossVectorHost?: string;
|
|
ossVectorPort?: string;
|
|
ossVectorUser?: string;
|
|
ossVectorPassword?: string;
|
|
ossVectorDbname?: string;
|
|
ossVectorDims?: string;
|
|
}
|
|
|
|
export function validateOssFlags(
|
|
flags: OssFlags,
|
|
): { error?: string } {
|
|
const llmId = flags.ossLlm || "openai";
|
|
const llmDef = LLM_PROVIDERS.find((p) => p.id === llmId);
|
|
if (!llmDef) return { error: `Unknown LLM provider: ${llmId}. Valid: ${LLM_PROVIDERS.map((p) => p.id).join(", ")}` };
|
|
|
|
if (llmDef.needsApiKey && !flags.ossLlmKey) {
|
|
return { error: `--oss-llm-key required when --oss-llm is ${llmId}` };
|
|
}
|
|
|
|
const embId = flags.ossEmbedder || "openai";
|
|
const embDef = EMBEDDER_PROVIDERS.find((p) => p.id === embId);
|
|
if (!embDef) return { error: `Unknown embedder provider: ${embId}. Valid: ${EMBEDDER_PROVIDERS.map((p) => p.id).join(", ")}` };
|
|
|
|
if (embDef.needsApiKey && !flags.ossEmbedderKey && !flags.ossLlmKey) {
|
|
return { error: `--oss-embedder-key required when --oss-embedder is ${embId}` };
|
|
}
|
|
|
|
const vecId = flags.ossVector || "qdrant";
|
|
const vecDef = VECTOR_PROVIDERS.find((p) => p.id === vecId);
|
|
if (!vecDef) return { error: `Unknown vector store provider: ${vecId}. Valid: ${VECTOR_PROVIDERS.map((p) => p.id).join(", ")}` };
|
|
|
|
if (vecId === "pgvector" && !flags.ossVectorUser) {
|
|
return { error: "--oss-vector-user required when --oss-vector is pgvector" };
|
|
}
|
|
|
|
return {};
|
|
}
|