|
|
|
@@ -54,6 +54,18 @@ function _newRequestId(): string {
|
|
|
|
|
return `brain_${Date.now().toString(36)}_${_nextId}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Mini-Query-String-Builder ohne URLSearchParams (Hermes-Polyfill kennt
|
|
|
|
|
* kein URLSearchParams.set, crasht). Akzeptiert object mit string/number/
|
|
|
|
|
* bool-Values; undefined/null/leere Strings werden ausgelassen. */
|
|
|
|
|
function _qs(params: Record<string, unknown>): string {
|
|
|
|
|
const parts: string[] = [];
|
|
|
|
|
for (const [k, v] of Object.entries(params)) {
|
|
|
|
|
if (v === undefined || v === null || v === '') continue;
|
|
|
|
|
parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
|
|
|
|
|
}
|
|
|
|
|
return parts.length ? `?${parts.join('&')}` : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface SendOpts {
|
|
|
|
|
method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
|
|
|
body?: AnyJson;
|
|
|
|
@@ -119,29 +131,31 @@ export const brainApi = {
|
|
|
|
|
|
|
|
|
|
/** Liste aller Memories, optional nach Type gefiltert. */
|
|
|
|
|
listMemories(opts: { type?: string; limit?: number } = {}): Promise<Memory[]> {
|
|
|
|
|
const qs = new URLSearchParams();
|
|
|
|
|
if (opts.type) qs.set('type', opts.type);
|
|
|
|
|
qs.set('limit', String(opts.limit || 500));
|
|
|
|
|
return _send(`/memory/list?${qs.toString()}`);
|
|
|
|
|
const qs = _qs({ type: opts.type, limit: opts.limit || 500 });
|
|
|
|
|
return _send(`/memory/list${qs}`);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/** Volltext-Substring-Suche. */
|
|
|
|
|
searchText(q: string, opts: { type?: string; includePinned?: boolean; k?: number } = {}): Promise<Memory[]> {
|
|
|
|
|
const qs = new URLSearchParams({ q });
|
|
|
|
|
if (opts.type) qs.set('type', opts.type);
|
|
|
|
|
qs.set('include_pinned', String(opts.includePinned !== false));
|
|
|
|
|
qs.set('k', String(opts.k || 50));
|
|
|
|
|
return _send(`/memory/search-text?${qs.toString()}`);
|
|
|
|
|
const qs = _qs({
|
|
|
|
|
q,
|
|
|
|
|
type: opts.type,
|
|
|
|
|
include_pinned: opts.includePinned !== false,
|
|
|
|
|
k: opts.k || 50,
|
|
|
|
|
});
|
|
|
|
|
return _send(`/memory/search-text${qs}`);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/** Semantische Suche (Embedder). */
|
|
|
|
|
searchSemantic(q: string, opts: { type?: string; includePinned?: boolean; k?: number; threshold?: number } = {}): Promise<Memory[]> {
|
|
|
|
|
const qs = new URLSearchParams({ q });
|
|
|
|
|
if (opts.type) qs.set('type', opts.type);
|
|
|
|
|
qs.set('include_pinned', String(opts.includePinned !== false));
|
|
|
|
|
qs.set('k', String(opts.k || 10));
|
|
|
|
|
qs.set('score_threshold', String(opts.threshold ?? 0.30));
|
|
|
|
|
return _send(`/memory/search?${qs.toString()}`);
|
|
|
|
|
const qs = _qs({
|
|
|
|
|
q,
|
|
|
|
|
type: opts.type,
|
|
|
|
|
include_pinned: opts.includePinned !== false,
|
|
|
|
|
k: opts.k || 10,
|
|
|
|
|
score_threshold: opts.threshold ?? 0.30,
|
|
|
|
|
});
|
|
|
|
|
return _send(`/memory/search${qs}`);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/** Memory anlegen. */
|
|
|
|
|