fix: adding login and fixing the plugin to follow the openclaw plugin standards (#4686)

This commit is contained in:
Kartik
2026-04-03 20:59:23 +05:30
committed by GitHub
parent 66230b3f1f
commit cfb3f58e4a
33 changed files with 5254 additions and 1376 deletions
+159 -13
View File
@@ -48,7 +48,10 @@ function normalizeAddResult(raw: any): AddResult {
memory: r.memory ?? r.text ?? "",
// Platform API may return PENDING status (async processing)
// OSS stores event in metadata.event
event: r.event ?? r.metadata?.event ?? (r.status === "PENDING" ? "ADD" : "ADD"),
event:
r.event ??
r.metadata?.event ??
(r.status === "PENDING" ? "ADD" : "ADD"),
})),
};
}
@@ -58,7 +61,10 @@ function normalizeAddResult(raw: any): AddResult {
results: raw.map((r: any) => ({
id: r.id ?? r.memory_id ?? "",
memory: r.memory ?? r.text ?? "",
event: r.event ?? r.metadata?.event ?? (r.status === "PENDING" ? "ADD" : "ADD"),
event:
r.event ??
r.metadata?.event ??
(r.status === "PENDING" ? "ADD" : "ADD"),
})),
};
}
@@ -77,7 +83,7 @@ class PlatformProvider implements Mem0Provider {
private readonly apiKey: string,
private readonly orgId?: string,
private readonly projectId?: string,
) { }
) {}
private async ensureClient(): Promise<void> {
if (this.client) return;
@@ -91,7 +97,9 @@ class PlatformProvider implements Mem0Provider {
private async _init(): Promise<void> {
const { default: MemoryClient } = await import("mem0ai");
const opts: { apiKey: string; org_id?: string; project_id?: string } = { apiKey: this.apiKey };
const opts: { apiKey: string; org_id?: string; project_id?: string } = {
apiKey: this.apiKey,
};
if (this.orgId) opts.org_id = this.orgId;
if (this.projectId) opts.project_id = this.projectId;
this.client = new MemoryClient(opts);
@@ -113,7 +121,8 @@ class PlatformProvider implements Mem0Provider {
if (options.source) opts.source = options.source;
// Agentic harness: direct storage bypass
if (options.infer !== undefined) opts.infer = options.infer;
if (options.deduced_memories) opts.deduced_memories = options.deduced_memories;
if (options.deduced_memories)
opts.deduced_memories = options.deduced_memories;
if (options.metadata) opts.metadata = options.metadata;
if (options.expiration_date) opts.expiration_date = options.expiration_date;
if (options.immutable) opts.immutable = options.immutable;
@@ -140,9 +149,11 @@ class PlatformProvider implements Mem0Provider {
};
if (options.top_k != null) opts.top_k = options.top_k;
if (options.threshold != null) opts.threshold = options.threshold;
if (options.keyword_search != null) opts.keyword_search = options.keyword_search;
if (options.keyword_search != null)
opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.rerank = options.reranking;
if (options.filter_memories != null) opts.filter_memories = options.filter_memories;
if (options.filter_memories != null)
opts.filter_memories = options.filter_memories;
if (options.categories != null) opts.categories = options.categories;
const results = await this.client.search(query, opts);
@@ -185,7 +196,17 @@ class PlatformProvider implements Mem0Provider {
await this.client.deleteAll({ user_id: userId });
}
async history(memoryId: string): Promise<Array<{ id: string; old_memory: string; new_memory: string; event: string; created_at: string }>> {
async history(
memoryId: string,
): Promise<
Array<{
id: string;
old_memory: string;
new_memory: string;
event: string;
created_at: string;
}>
> {
await this.ensureClient();
const result = await this.client.history(memoryId);
return Array.isArray(result) ? result : [];
@@ -204,7 +225,7 @@ class OSSProvider implements Mem0Provider {
private readonly ossConfig?: Mem0Config["oss"],
private readonly customPrompt?: string,
private readonly resolvePath?: (p: string) => string,
) { }
) {}
private async ensureMemory(): Promise<void> {
if (this.memory) return;
@@ -257,6 +278,18 @@ class OSSProvider implements Mem0Provider {
throw err;
}
}
// Force the SDK's internal auto-initialization to complete now.
// Without this, concurrent method calls (e.g. auto-recall + stats)
// both trigger _autoInitialize() simultaneously, causing PGVector's
// pg client to call connect() twice → "Client has already been
// connected" crash. (#4638)
try {
await this.memory.getAll({ userId: "__mem0_warmup__" });
} catch {
// Warmup errors are non-fatal — the SDK may still work for
// subsequent calls once its internal state settles.
}
}
async add(
@@ -271,7 +304,8 @@ class OSSProvider implements Mem0Provider {
// Agentic harness: direct storage bypass
if (options.infer !== undefined) addOpts.infer = options.infer;
if (options.metadata) addOpts.metadata = options.metadata;
if (options.expiration_date) addOpts.expirationDate = options.expiration_date;
if (options.expiration_date)
addOpts.expirationDate = options.expiration_date;
if (options.immutable) addOpts.immutable = options.immutable;
// OSS SDK doesn't support deduced_memories — when infer=false, it stores
@@ -296,7 +330,8 @@ class OSSProvider implements Mem0Provider {
if (options.run_id) opts.runId = options.run_id;
if (options.limit != null) opts.limit = options.limit;
else if (options.top_k != null) opts.limit = options.top_k;
if (options.keyword_search != null) opts.keyword_search = options.keyword_search;
if (options.keyword_search != null)
opts.keyword_search = options.keyword_search;
if (options.reranking != null) opts.reranking = options.reranking;
if (options.source) opts.source = options.source;
if (options.threshold != null) opts.threshold = options.threshold;
@@ -306,7 +341,9 @@ class OSSProvider implements Mem0Provider {
// Filter results by threshold if specified (client-side filtering as fallback)
if (options.threshold != null) {
return normalized.filter(item => (item.score ?? 0) >= options.threshold!);
return normalized.filter(
(item) => (item.score ?? 0) >= options.threshold!,
);
}
return normalized;
@@ -346,7 +383,17 @@ class OSSProvider implements Mem0Provider {
await this.memory.deleteAll({ userId });
}
async history(memoryId: string): Promise<Array<{ id: string; old_memory: string; new_memory: string; event: string; created_at: string }>> {
async history(
memoryId: string,
): Promise<
Array<{
id: string;
old_memory: string;
new_memory: string;
event: string;
created_at: string;
}>
> {
await this.ensureMemory();
try {
const result = await this.memory.history(memoryId);
@@ -374,3 +421,102 @@ export function createProvider(
return new PlatformProvider(cfg.apiKey!, cfg.orgId, cfg.projectId);
}
// ============================================================================
// Provider-to-Backend Adapter
// ============================================================================
import type { Backend } from "./backend/base.ts";
/**
* Wraps an existing Mem0Provider as the Backend interface.
* Used in OSS mode where PlatformBackend cannot be used.
* Platform-only methods (entities, events) throw clear errors.
*/
export function providerToBackend(
provider: Mem0Provider,
userId: string,
): Backend {
return {
async add(content, messages, opts = {}) {
const msgs = messages ?? (content ? [{ role: "user", content }] : []);
const result = await provider.add(
msgs as Array<{ role: string; content: string }>,
{
user_id: opts.userId ?? userId,
...(opts.runId && { run_id: opts.runId }),
...(opts.metadata && { metadata: opts.metadata }),
...(opts.immutable && { immutable: true }),
...(opts.infer === false && { infer: false }),
...(opts.expires && { expiration_date: opts.expires }),
...(opts.enableGraph && { enable_graph: true }),
},
);
return result as unknown as Record<string, unknown>;
},
async search(query, opts = {}) {
const results = await provider.search(query, {
user_id: opts.userId ?? userId,
top_k: opts.topK,
threshold: opts.threshold,
keyword_search: opts.keyword,
reranking: opts.rerank,
filters: opts.filters,
});
return results as unknown as Record<string, unknown>[];
},
async get(memoryId) {
const item = await provider.get(memoryId);
return item as unknown as Record<string, unknown>;
},
async listMemories(opts = {}) {
const items = await provider.getAll({
user_id: opts.userId ?? userId,
page_size: opts.pageSize,
});
return items as unknown as Record<string, unknown>[];
},
async update(memoryId, content, metadata) {
if (content) await provider.update(memoryId, content);
if (metadata) {
// OSS provider doesn't support metadata-only updates — log warning
console.warn(
"providerToBackend: metadata updates are not supported in OSS mode, only text updates are applied",
);
}
return { id: memoryId, updated: true };
},
async delete(memoryId, opts = {}) {
if (opts.all) {
await provider.deleteAll(opts.userId ?? userId);
return { deleted: "all" };
}
if (memoryId) {
await provider.delete(memoryId);
return { deleted: memoryId };
}
throw new Error("Either memoryId or all is required");
},
async deleteEntities() {
throw new Error("Entity management is only available in platform mode.");
},
async status() {
return { connected: true, backend: "oss" };
},
async entities() {
throw new Error("Entity management is only available in platform mode.");
},
async listEvents() {
throw new Error("Event management is only available in platform mode.");
},
async getEvent() {
throw new Error("Event management is only available in platform mode.");
},
};
}