fix: adding login and fixing the plugin to follow the openclaw plugin standards (#4686)
This commit is contained in:
+159
-13
@@ -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.");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user