@props-labs/mesh-os 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/client.d.ts +23 -4
- package/dist/core/client.js +69 -37
- package/dist/core/entities.d.ts +58 -0
- package/dist/core/entities.js +349 -0
- package/dist/core/taxonomy.d.ts +85 -1
- package/dist/core/taxonomy.js +49 -1
- package/package.json +1 -1
- package/src/templates/hasura/migrations/default/4_remove_slug_validation/down.sql +20 -0
- package/src/templates/hasura/migrations/default/4_remove_slug_validation/up.sql +5 -0
- package/src/templates/hasura/migrations/default/5_entities/down.sql +13 -0
- package/src/templates/hasura/migrations/default/5_entities/up.sql +155 -0
package/dist/core/client.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter } from './taxonomy';
|
1
|
+
import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter, EntityRelationshipType } from './taxonomy';
|
2
|
+
import { EntityManager } from './entities';
|
2
3
|
/**
|
3
4
|
* An agent in the system.
|
4
5
|
*/
|
@@ -56,6 +57,17 @@ export interface MeshOSConfig {
|
|
56
57
|
apiKey?: string;
|
57
58
|
openaiApiKey?: string;
|
58
59
|
}
|
60
|
+
/**
|
61
|
+
* Memory creation options with entity support.
|
62
|
+
*/
|
63
|
+
export interface RememberOptions {
|
64
|
+
content: string;
|
65
|
+
agentId: string;
|
66
|
+
metadata?: Partial<MemoryMetadata>;
|
67
|
+
expiresAt?: string;
|
68
|
+
entityId?: string;
|
69
|
+
entityRelationship?: EntityRelationshipType;
|
70
|
+
}
|
59
71
|
/**
|
60
72
|
* MeshOS client for interacting with the system.
|
61
73
|
*/
|
@@ -63,6 +75,7 @@ export declare class MeshOS {
|
|
63
75
|
private url;
|
64
76
|
private headers;
|
65
77
|
private openai;
|
78
|
+
entities: EntityManager;
|
66
79
|
constructor(config?: MeshOSConfig);
|
67
80
|
private validateSlug;
|
68
81
|
/**
|
@@ -90,9 +103,13 @@ export declare class MeshOS {
|
|
90
103
|
*/
|
91
104
|
getAgent(agentId: string): Promise<Agent | null>;
|
92
105
|
/**
|
93
|
-
* Store a new memory.
|
106
|
+
* Store a new memory with optional entity linking.
|
107
|
+
*/
|
108
|
+
remember(options: RememberOptions | string, agentId?: string, metadata?: Partial<MemoryMetadata>, expiresAt?: string): Promise<Memory | Memory[]>;
|
109
|
+
/**
|
110
|
+
* Internal method to store a memory.
|
94
111
|
*/
|
95
|
-
|
112
|
+
private _remember;
|
96
113
|
/**
|
97
114
|
* Delete a specific memory.
|
98
115
|
*/
|
@@ -124,7 +141,7 @@ export declare class MeshOS {
|
|
124
141
|
*/
|
125
142
|
private expandQuery;
|
126
143
|
/**
|
127
|
-
* Search memories by semantic similarity.
|
144
|
+
* Search memories by semantic similarity with optional entity filtering.
|
128
145
|
*/
|
129
146
|
recall(options: {
|
130
147
|
query: string;
|
@@ -137,6 +154,8 @@ export declare class MeshOS {
|
|
137
154
|
filters?: Record<string, unknown>;
|
138
155
|
createdAtFilter?: TimestampFilter;
|
139
156
|
expiresAtFilter?: TimestampFilter;
|
157
|
+
entityId?: string;
|
158
|
+
entityRelationship?: EntityRelationshipType;
|
140
159
|
}): Promise<Memory[]>;
|
141
160
|
/**
|
142
161
|
* Internal method to perform recall with a specific threshold.
|
package/dist/core/client.js
CHANGED
@@ -11,6 +11,7 @@ const openai_1 = __importDefault(require("openai"));
|
|
11
11
|
const chalk_1 = __importDefault(require("chalk"));
|
12
12
|
const boxen_1 = __importDefault(require("boxen"));
|
13
13
|
const taxonomy_1 = require("./taxonomy");
|
14
|
+
const entities_1 = require("./entities");
|
14
15
|
// Constants
|
15
16
|
const SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
16
17
|
/**
|
@@ -60,6 +61,8 @@ class MeshOS {
|
|
60
61
|
throw new Error('OpenAI API key is required');
|
61
62
|
}
|
62
63
|
this.openai = new openai_1.default({ apiKey: openaiApiKey });
|
64
|
+
// Initialize entity manager
|
65
|
+
this.entities = new entities_1.EntityManager(this.url, this.headers, this.createEmbedding.bind(this));
|
63
66
|
}
|
64
67
|
validateSlug(slug) {
|
65
68
|
return SLUG_PATTERN.test(slug);
|
@@ -195,9 +198,32 @@ class MeshOS {
|
|
195
198
|
return result.agents_by_pk;
|
196
199
|
}
|
197
200
|
/**
|
198
|
-
* Store a new memory.
|
201
|
+
* Store a new memory with optional entity linking.
|
199
202
|
*/
|
200
|
-
async remember(
|
203
|
+
async remember(options, agentId, metadata, expiresAt) {
|
204
|
+
// Handle legacy method signature
|
205
|
+
if (typeof options === 'string') {
|
206
|
+
options = {
|
207
|
+
content: options,
|
208
|
+
agentId: agentId,
|
209
|
+
metadata,
|
210
|
+
expiresAt
|
211
|
+
};
|
212
|
+
}
|
213
|
+
// Create the memory
|
214
|
+
const result = await this._remember(options.content, options.agentId, options.metadata, options.expiresAt);
|
215
|
+
// If entity linking is requested, create the link
|
216
|
+
if (options.entityId) {
|
217
|
+
const memories = Array.isArray(result) ? result : [result];
|
218
|
+
const firstMemory = memories[0];
|
219
|
+
await this.entities.linkMemory(options.entityId, firstMemory.id, options.entityRelationship || taxonomy_1.EntityRelationshipType.ASSOCIATED);
|
220
|
+
}
|
221
|
+
return result;
|
222
|
+
}
|
223
|
+
/**
|
224
|
+
* Internal method to store a memory.
|
225
|
+
*/
|
226
|
+
async _remember(content, agentId, metadata, expiresAt) {
|
201
227
|
// Create default metadata if none provided
|
202
228
|
const fullMetadata = taxonomy_1.memoryMetadataSchema.parse({
|
203
229
|
type: taxonomy_1.DataType.KNOWLEDGE,
|
@@ -528,36 +554,42 @@ class MeshOS {
|
|
528
554
|
return result.slice(0, numVariations + 1);
|
529
555
|
}
|
530
556
|
/**
|
531
|
-
* Search memories by semantic similarity.
|
557
|
+
* Search memories by semantic similarity with optional entity filtering.
|
532
558
|
*/
|
533
559
|
async recall(options) {
|
534
|
-
const
|
535
|
-
// First try: Direct search with initial threshold
|
560
|
+
const minResults = options.minResults || 1;
|
536
561
|
let results = await this.recallWithThreshold({
|
537
|
-
query,
|
538
|
-
threshold,
|
539
|
-
agentId,
|
540
|
-
limit,
|
541
|
-
filters,
|
542
|
-
createdAtFilter,
|
543
|
-
expiresAtFilter
|
562
|
+
query: options.query,
|
563
|
+
threshold: options.threshold || 0.7,
|
564
|
+
agentId: options.agentId,
|
565
|
+
limit: options.limit,
|
566
|
+
filters: options.filters,
|
567
|
+
createdAtFilter: options.createdAtFilter,
|
568
|
+
expiresAtFilter: options.expiresAtFilter
|
544
569
|
});
|
570
|
+
// If entity filtering is requested, filter results
|
571
|
+
if (options.entityId) {
|
572
|
+
const linkedMemories = await this.entities.getLinkedMemories(options.entityId, options.entityRelationship);
|
573
|
+
const linkedMemoryIds = new Set(linkedMemories.map(m => m.id));
|
574
|
+
results = results.filter(memory => linkedMemoryIds.has(memory.id));
|
575
|
+
}
|
576
|
+
// First try: Direct search with initial threshold
|
545
577
|
if (results.length >= minResults) {
|
546
|
-
return results.slice(0, limit);
|
578
|
+
return results.slice(0, options.limit || 5);
|
547
579
|
}
|
548
580
|
// Second try: If adaptive threshold is enabled, try lowering the threshold
|
549
|
-
if (adaptiveThreshold) {
|
550
|
-
let currentThreshold = threshold - 0.05;
|
581
|
+
if (options.adaptiveThreshold) {
|
582
|
+
let currentThreshold = (options.threshold || 0.7) - 0.05;
|
551
583
|
const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
|
552
584
|
while (currentThreshold >= minThreshold && results.length < minResults) {
|
553
585
|
const newResults = await this.recallWithThreshold({
|
554
|
-
query,
|
586
|
+
query: options.query,
|
555
587
|
threshold: currentThreshold,
|
556
|
-
agentId,
|
557
|
-
limit,
|
558
|
-
filters,
|
559
|
-
createdAtFilter,
|
560
|
-
expiresAtFilter
|
588
|
+
agentId: options.agentId,
|
589
|
+
limit: options.limit,
|
590
|
+
filters: options.filters,
|
591
|
+
createdAtFilter: options.createdAtFilter,
|
592
|
+
expiresAtFilter: options.expiresAtFilter
|
561
593
|
});
|
562
594
|
// Add new results that aren't already in the list
|
563
595
|
for (const result of newResults) {
|
@@ -572,20 +604,20 @@ class MeshOS {
|
|
572
604
|
}
|
573
605
|
}
|
574
606
|
// Third try: If we still don't have ANY results and semantic expansion is enabled
|
575
|
-
if (results.length === 0 && useSemanticExpansion) {
|
576
|
-
const variations = await this.expandQuery(query);
|
607
|
+
if (results.length === 0 && options.useSemanticExpansion) {
|
608
|
+
const variations = await this.expandQuery(options.query);
|
577
609
|
const seenIds = new Map();
|
578
610
|
const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
|
579
611
|
// Try each variation with the original threshold first
|
580
612
|
for (const variation of variations.slice(1)) { // Skip original query as we already tried it
|
581
613
|
const variationResults = await this.recallWithThreshold({
|
582
614
|
query: variation,
|
583
|
-
threshold,
|
584
|
-
agentId,
|
585
|
-
limit,
|
586
|
-
filters,
|
587
|
-
createdAtFilter,
|
588
|
-
expiresAtFilter
|
615
|
+
threshold: options.threshold || 0.7,
|
616
|
+
agentId: options.agentId,
|
617
|
+
limit: options.limit,
|
618
|
+
filters: options.filters,
|
619
|
+
createdAtFilter: options.createdAtFilter,
|
620
|
+
expiresAtFilter: options.expiresAtFilter
|
589
621
|
});
|
590
622
|
// Add new results or update if better similarity
|
591
623
|
for (const memory of variationResults) {
|
@@ -599,18 +631,18 @@ class MeshOS {
|
|
599
631
|
}
|
600
632
|
}
|
601
633
|
// If still no results, try variations with adaptive threshold
|
602
|
-
if (seenIds.size === 0 && adaptiveThreshold) {
|
603
|
-
let currentThreshold = threshold - 0.05;
|
634
|
+
if (seenIds.size === 0 && options.adaptiveThreshold) {
|
635
|
+
let currentThreshold = (options.threshold || 0.7) - 0.05;
|
604
636
|
while (currentThreshold >= minThreshold && seenIds.size === 0) {
|
605
637
|
for (const variation of variations.slice(1)) {
|
606
638
|
const variationResults = await this.recallWithThreshold({
|
607
639
|
query: variation,
|
608
640
|
threshold: currentThreshold,
|
609
|
-
agentId,
|
610
|
-
limit,
|
611
|
-
filters,
|
612
|
-
createdAtFilter,
|
613
|
-
expiresAtFilter
|
641
|
+
agentId: options.agentId,
|
642
|
+
limit: options.limit,
|
643
|
+
filters: options.filters,
|
644
|
+
createdAtFilter: options.createdAtFilter,
|
645
|
+
expiresAtFilter: options.expiresAtFilter
|
614
646
|
});
|
615
647
|
for (const memory of variationResults) {
|
616
648
|
const existingMemory = seenIds.get(memory.id);
|
@@ -635,7 +667,7 @@ class MeshOS {
|
|
635
667
|
}
|
636
668
|
// Sort by similarity and return top results
|
637
669
|
results.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
|
638
|
-
return results.slice(0, limit);
|
670
|
+
return results.slice(0, options.limit || 5);
|
639
671
|
}
|
640
672
|
/**
|
641
673
|
* Internal method to perform recall with a specific threshold.
|
@@ -0,0 +1,58 @@
|
|
1
|
+
/**
|
2
|
+
* Entity management functionality for MeshOS.
|
3
|
+
*/
|
4
|
+
import { Entity, EntityStatus, EntityRelationshipType, EntityMemoryLink } from './taxonomy';
|
5
|
+
import type { Memory } from './client';
|
6
|
+
export declare class EntityManager {
|
7
|
+
private url;
|
8
|
+
private headers;
|
9
|
+
private createEmbedding;
|
10
|
+
constructor(url: string, headers: Record<string, string>, createEmbedding: (text: string) => Promise<number[]>);
|
11
|
+
/**
|
12
|
+
* Execute a GraphQL query.
|
13
|
+
*/
|
14
|
+
private executeQuery;
|
15
|
+
/**
|
16
|
+
* Add a new entity.
|
17
|
+
*/
|
18
|
+
add(entity: Omit<Entity, 'id' | 'createdAt' | 'updatedAt'>): Promise<Entity>;
|
19
|
+
/**
|
20
|
+
* Get an entity by ID.
|
21
|
+
*/
|
22
|
+
get(id: string): Promise<Entity | null>;
|
23
|
+
/**
|
24
|
+
* Get an entity by reference ID.
|
25
|
+
*/
|
26
|
+
getByRefId(refId: string): Promise<Entity | null>;
|
27
|
+
/**
|
28
|
+
* Update an entity.
|
29
|
+
*/
|
30
|
+
update(id: string, updates: Partial<Omit<Entity, 'id' | 'createdAt' | 'updatedAt'>>): Promise<Entity>;
|
31
|
+
/**
|
32
|
+
* Delete an entity.
|
33
|
+
*/
|
34
|
+
delete(id: string): Promise<boolean>;
|
35
|
+
/**
|
36
|
+
* Search entities by semantic similarity.
|
37
|
+
*/
|
38
|
+
search(options: {
|
39
|
+
query: string;
|
40
|
+
threshold?: number;
|
41
|
+
limit?: number;
|
42
|
+
type?: string;
|
43
|
+
status?: EntityStatus;
|
44
|
+
}): Promise<(Entity & {
|
45
|
+
similarity?: number;
|
46
|
+
})[]>;
|
47
|
+
/**
|
48
|
+
* Link an entity to a memory.
|
49
|
+
*/
|
50
|
+
linkMemory(entityId: string, memoryId: string, relationship: EntityRelationshipType, confidence?: number, metadata?: Record<string, unknown>): Promise<EntityMemoryLink>;
|
51
|
+
/**
|
52
|
+
* Get all memories linked to an entity.
|
53
|
+
*/
|
54
|
+
getLinkedMemories(entityId: string, relationship?: EntityRelationshipType): Promise<Array<Memory & {
|
55
|
+
relationship: EntityRelationshipType;
|
56
|
+
confidence: number;
|
57
|
+
}>>;
|
58
|
+
}
|
@@ -0,0 +1,349 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.EntityManager = void 0;
|
4
|
+
/**
|
5
|
+
* Entity management functionality for MeshOS.
|
6
|
+
*/
|
7
|
+
const taxonomy_1 = require("./taxonomy");
|
8
|
+
class EntityManager {
|
9
|
+
constructor(url, headers, createEmbedding) {
|
10
|
+
this.url = url;
|
11
|
+
this.headers = headers;
|
12
|
+
this.createEmbedding = createEmbedding;
|
13
|
+
}
|
14
|
+
/**
|
15
|
+
* Execute a GraphQL query.
|
16
|
+
*/
|
17
|
+
async executeQuery(query, variables) {
|
18
|
+
const response = await fetch(this.url, {
|
19
|
+
method: 'POST',
|
20
|
+
headers: this.headers,
|
21
|
+
body: JSON.stringify({
|
22
|
+
query,
|
23
|
+
variables: variables || {}
|
24
|
+
})
|
25
|
+
});
|
26
|
+
if (!response.ok) {
|
27
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
28
|
+
}
|
29
|
+
const result = await response.json();
|
30
|
+
if (result.errors) {
|
31
|
+
throw new Error(result.errors[0].message);
|
32
|
+
}
|
33
|
+
return result.data;
|
34
|
+
}
|
35
|
+
/**
|
36
|
+
* Add a new entity.
|
37
|
+
*/
|
38
|
+
async add(entity) {
|
39
|
+
// Validate entity data
|
40
|
+
const validatedData = taxonomy_1.entitySchema.omit({ id: true, createdAt: true, updatedAt: true }).parse(entity);
|
41
|
+
// Create embedding from name and description
|
42
|
+
const embeddingText = `${validatedData.name} ${validatedData.description || ''}`.trim();
|
43
|
+
const embedding = await this.createEmbedding(embeddingText);
|
44
|
+
const embeddingStr = `[${embedding.join(',')}]`;
|
45
|
+
const query = `
|
46
|
+
mutation AddEntity($entity: entities_insert_input!) {
|
47
|
+
insert_entities_one(object: $entity) {
|
48
|
+
id
|
49
|
+
ref_id
|
50
|
+
type
|
51
|
+
name
|
52
|
+
description
|
53
|
+
metadata
|
54
|
+
status
|
55
|
+
created_at
|
56
|
+
updated_at
|
57
|
+
}
|
58
|
+
}
|
59
|
+
`;
|
60
|
+
const result = await this.executeQuery(query, {
|
61
|
+
entity: {
|
62
|
+
ref_id: validatedData.refId,
|
63
|
+
type: validatedData.type,
|
64
|
+
name: validatedData.name,
|
65
|
+
description: validatedData.description,
|
66
|
+
metadata: validatedData.metadata,
|
67
|
+
status: validatedData.status,
|
68
|
+
embedding: embeddingStr
|
69
|
+
}
|
70
|
+
});
|
71
|
+
// Convert snake_case to camelCase
|
72
|
+
const { ref_id, created_at, updated_at, ...rest } = result.insert_entities_one;
|
73
|
+
return {
|
74
|
+
...rest,
|
75
|
+
refId: ref_id,
|
76
|
+
createdAt: created_at,
|
77
|
+
updatedAt: updated_at,
|
78
|
+
};
|
79
|
+
}
|
80
|
+
/**
|
81
|
+
* Get an entity by ID.
|
82
|
+
*/
|
83
|
+
async get(id) {
|
84
|
+
const query = `
|
85
|
+
query GetEntity($id: uuid!) {
|
86
|
+
entities_by_pk(id: $id) {
|
87
|
+
id
|
88
|
+
ref_id
|
89
|
+
type
|
90
|
+
name
|
91
|
+
description
|
92
|
+
metadata
|
93
|
+
status
|
94
|
+
created_at
|
95
|
+
updated_at
|
96
|
+
}
|
97
|
+
}
|
98
|
+
`;
|
99
|
+
const result = await this.executeQuery(query, { id });
|
100
|
+
if (!result.entities_by_pk) {
|
101
|
+
return null;
|
102
|
+
}
|
103
|
+
// Convert snake_case to camelCase
|
104
|
+
const { ref_id, created_at, updated_at, ...rest } = result.entities_by_pk;
|
105
|
+
return {
|
106
|
+
...rest,
|
107
|
+
refId: ref_id,
|
108
|
+
createdAt: created_at,
|
109
|
+
updatedAt: updated_at,
|
110
|
+
};
|
111
|
+
}
|
112
|
+
/**
|
113
|
+
* Get an entity by reference ID.
|
114
|
+
*/
|
115
|
+
async getByRefId(refId) {
|
116
|
+
const query = `
|
117
|
+
query GetEntityByRefId($refId: String!) {
|
118
|
+
entities(where: {ref_id: {_eq: $refId}}, limit: 1) {
|
119
|
+
id
|
120
|
+
ref_id
|
121
|
+
type
|
122
|
+
name
|
123
|
+
description
|
124
|
+
metadata
|
125
|
+
status
|
126
|
+
created_at
|
127
|
+
updated_at
|
128
|
+
}
|
129
|
+
}
|
130
|
+
`;
|
131
|
+
const result = await this.executeQuery(query, { refId });
|
132
|
+
if (!result.entities[0]) {
|
133
|
+
return null;
|
134
|
+
}
|
135
|
+
// Convert snake_case to camelCase
|
136
|
+
const { ref_id, created_at, updated_at, ...rest } = result.entities[0];
|
137
|
+
return {
|
138
|
+
...rest,
|
139
|
+
refId: ref_id,
|
140
|
+
createdAt: created_at,
|
141
|
+
updatedAt: updated_at,
|
142
|
+
};
|
143
|
+
}
|
144
|
+
/**
|
145
|
+
* Update an entity.
|
146
|
+
*/
|
147
|
+
async update(id, updates) {
|
148
|
+
// If name or description is updated, update embedding
|
149
|
+
let embedding;
|
150
|
+
if (updates.name || updates.description) {
|
151
|
+
const entity = await this.get(id);
|
152
|
+
if (!entity) {
|
153
|
+
throw new Error(`Entity ${id} not found`);
|
154
|
+
}
|
155
|
+
const embeddingText = `${updates.name || entity.name} ${updates.description || entity.description || ''}`.trim();
|
156
|
+
embedding = await this.createEmbedding(embeddingText);
|
157
|
+
}
|
158
|
+
const query = `
|
159
|
+
mutation UpdateEntity($id: uuid!, $updates: entities_set_input!) {
|
160
|
+
update_entities_by_pk(pk_columns: {id: $id}, _set: $updates) {
|
161
|
+
id
|
162
|
+
ref_id
|
163
|
+
type
|
164
|
+
name
|
165
|
+
description
|
166
|
+
metadata
|
167
|
+
status
|
168
|
+
created_at
|
169
|
+
updated_at
|
170
|
+
}
|
171
|
+
}
|
172
|
+
`;
|
173
|
+
const updateData = {
|
174
|
+
ref_id: updates.refId,
|
175
|
+
type: updates.type,
|
176
|
+
name: updates.name,
|
177
|
+
description: updates.description,
|
178
|
+
metadata: updates.metadata,
|
179
|
+
status: updates.status,
|
180
|
+
};
|
181
|
+
if (embedding) {
|
182
|
+
updateData.embedding = `[${embedding.join(',')}]`;
|
183
|
+
}
|
184
|
+
// Remove undefined values
|
185
|
+
Object.keys(updateData).forEach(key => updateData[key] === undefined && delete updateData[key]);
|
186
|
+
const result = await this.executeQuery(query, {
|
187
|
+
id,
|
188
|
+
updates: updateData
|
189
|
+
});
|
190
|
+
// Convert snake_case to camelCase
|
191
|
+
const { ref_id, created_at, updated_at, ...rest } = result.update_entities_by_pk;
|
192
|
+
return {
|
193
|
+
...rest,
|
194
|
+
refId: ref_id,
|
195
|
+
createdAt: created_at,
|
196
|
+
updatedAt: updated_at,
|
197
|
+
};
|
198
|
+
}
|
199
|
+
/**
|
200
|
+
* Delete an entity.
|
201
|
+
*/
|
202
|
+
async delete(id) {
|
203
|
+
const query = `
|
204
|
+
mutation DeleteEntity($id: uuid!) {
|
205
|
+
delete_entities_by_pk(id: $id) {
|
206
|
+
id
|
207
|
+
}
|
208
|
+
}
|
209
|
+
`;
|
210
|
+
const result = await this.executeQuery(query, { id });
|
211
|
+
return result.delete_entities_by_pk !== null;
|
212
|
+
}
|
213
|
+
/**
|
214
|
+
* Search entities by semantic similarity.
|
215
|
+
*/
|
216
|
+
async search(options) {
|
217
|
+
const { query, threshold = 0.7, limit = 10, type, status } = options;
|
218
|
+
// Create embedding for search query
|
219
|
+
const embedding = await this.createEmbedding(query);
|
220
|
+
const embeddingStr = `[${embedding.join(',')}]`;
|
221
|
+
const searchQuery = `
|
222
|
+
query SearchEntities(
|
223
|
+
$embedding: vector!,
|
224
|
+
$threshold: float8!,
|
225
|
+
$limit: Int!,
|
226
|
+
$type: String,
|
227
|
+
$status: String
|
228
|
+
) {
|
229
|
+
entities(
|
230
|
+
where: {
|
231
|
+
_and: [
|
232
|
+
{type: {_eq: $type}},
|
233
|
+
{status: {_eq: $status}}
|
234
|
+
]
|
235
|
+
},
|
236
|
+
order_by: {embedding: {_cosine_distance: $embedding}},
|
237
|
+
limit: $limit
|
238
|
+
) {
|
239
|
+
id
|
240
|
+
ref_id
|
241
|
+
type
|
242
|
+
name
|
243
|
+
description
|
244
|
+
metadata
|
245
|
+
status
|
246
|
+
created_at
|
247
|
+
updated_at
|
248
|
+
similarity: _cosine_similarity_embedding_with($embedding)
|
249
|
+
}
|
250
|
+
}
|
251
|
+
`;
|
252
|
+
const result = await this.executeQuery(searchQuery, {
|
253
|
+
embedding: embeddingStr,
|
254
|
+
threshold,
|
255
|
+
limit,
|
256
|
+
type,
|
257
|
+
status
|
258
|
+
});
|
259
|
+
return result.entities.map(entity => {
|
260
|
+
const { ref_id, created_at, updated_at, ...rest } = entity;
|
261
|
+
return {
|
262
|
+
...rest,
|
263
|
+
refId: ref_id,
|
264
|
+
createdAt: created_at,
|
265
|
+
updatedAt: updated_at,
|
266
|
+
};
|
267
|
+
}).filter(entity => (entity.similarity || 0) >= threshold);
|
268
|
+
}
|
269
|
+
/**
|
270
|
+
* Link an entity to a memory.
|
271
|
+
*/
|
272
|
+
async linkMemory(entityId, memoryId, relationship, confidence, metadata) {
|
273
|
+
const query = `
|
274
|
+
mutation LinkEntityMemory($link: entity_memory_links_insert_input!) {
|
275
|
+
insert_entity_memory_links_one(object: $link) {
|
276
|
+
id
|
277
|
+
entity_id
|
278
|
+
memory_id
|
279
|
+
relationship
|
280
|
+
confidence
|
281
|
+
metadata
|
282
|
+
created_at
|
283
|
+
}
|
284
|
+
}
|
285
|
+
`;
|
286
|
+
const result = await this.executeQuery(query, {
|
287
|
+
link: {
|
288
|
+
entity_id: entityId,
|
289
|
+
memory_id: memoryId,
|
290
|
+
relationship,
|
291
|
+
confidence: confidence || 1.0,
|
292
|
+
metadata: metadata || {}
|
293
|
+
}
|
294
|
+
});
|
295
|
+
// Convert snake_case to camelCase
|
296
|
+
const { entity_id, memory_id, created_at, ...rest } = result.insert_entity_memory_links_one;
|
297
|
+
return {
|
298
|
+
...rest,
|
299
|
+
entityId: entity_id,
|
300
|
+
memoryId: memory_id,
|
301
|
+
createdAt: created_at,
|
302
|
+
};
|
303
|
+
}
|
304
|
+
/**
|
305
|
+
* Get all memories linked to an entity.
|
306
|
+
*/
|
307
|
+
async getLinkedMemories(entityId, relationship) {
|
308
|
+
const query = `
|
309
|
+
query GetLinkedMemories($entityId: uuid!, $relationship: String) {
|
310
|
+
entity_memory_links(
|
311
|
+
where: {
|
312
|
+
entity_id: {_eq: $entityId},
|
313
|
+
relationship: {_eq: $relationship}
|
314
|
+
}
|
315
|
+
) {
|
316
|
+
memory {
|
317
|
+
id
|
318
|
+
agent_id
|
319
|
+
content
|
320
|
+
metadata
|
321
|
+
created_at
|
322
|
+
updated_at
|
323
|
+
expires_at
|
324
|
+
}
|
325
|
+
relationship
|
326
|
+
confidence
|
327
|
+
}
|
328
|
+
}
|
329
|
+
`;
|
330
|
+
const result = await this.executeQuery(query, {
|
331
|
+
entityId,
|
332
|
+
relationship
|
333
|
+
});
|
334
|
+
return result.entity_memory_links.map(link => {
|
335
|
+
const { memory, relationship, confidence } = link;
|
336
|
+
const { agent_id, created_at, updated_at, expires_at, ...rest } = memory;
|
337
|
+
return {
|
338
|
+
...rest,
|
339
|
+
agentId: agent_id,
|
340
|
+
createdAt: created_at,
|
341
|
+
updatedAt: updated_at,
|
342
|
+
expiresAt: expires_at,
|
343
|
+
relationship,
|
344
|
+
confidence
|
345
|
+
};
|
346
|
+
});
|
347
|
+
}
|
348
|
+
}
|
349
|
+
exports.EntityManager = EntityManager;
|
package/dist/core/taxonomy.d.ts
CHANGED
@@ -11,6 +11,90 @@ export declare enum DataType {
|
|
11
11
|
DECISION = "decision",// Strategic & operational choices
|
12
12
|
MEDIA = "media"
|
13
13
|
}
|
14
|
+
/**
|
15
|
+
* Entity relationship types.
|
16
|
+
*/
|
17
|
+
export declare enum EntityRelationshipType {
|
18
|
+
ASSOCIATED = "associated",// Generic association
|
19
|
+
OWNS = "owns",// Ownership relationship
|
20
|
+
REFERENCES = "references",// Reference relationship
|
21
|
+
MENTIONS = "mentions",// Mention relationship
|
22
|
+
DESCRIBES = "describes",// Description relationship
|
23
|
+
PART_OF = "part_of"
|
24
|
+
}
|
25
|
+
/**
|
26
|
+
* Entity status types.
|
27
|
+
*/
|
28
|
+
export declare enum EntityStatus {
|
29
|
+
ACTIVE = "active",
|
30
|
+
INACTIVE = "inactive",
|
31
|
+
ARCHIVED = "archived",
|
32
|
+
DELETED = "deleted"
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Entity schema definition.
|
36
|
+
*/
|
37
|
+
export declare const entitySchema: z.ZodObject<{
|
38
|
+
id: z.ZodOptional<z.ZodString>;
|
39
|
+
refId: z.ZodOptional<z.ZodString>;
|
40
|
+
type: z.ZodString;
|
41
|
+
name: z.ZodString;
|
42
|
+
description: z.ZodOptional<z.ZodString>;
|
43
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
44
|
+
status: z.ZodDefault<z.ZodNativeEnum<typeof EntityStatus>>;
|
45
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
46
|
+
updatedAt: z.ZodOptional<z.ZodString>;
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
48
|
+
type: string;
|
49
|
+
name: string;
|
50
|
+
status: EntityStatus;
|
51
|
+
metadata: Record<string, unknown>;
|
52
|
+
id?: string | undefined;
|
53
|
+
refId?: string | undefined;
|
54
|
+
description?: string | undefined;
|
55
|
+
createdAt?: string | undefined;
|
56
|
+
updatedAt?: string | undefined;
|
57
|
+
}, {
|
58
|
+
type: string;
|
59
|
+
name: string;
|
60
|
+
id?: string | undefined;
|
61
|
+
refId?: string | undefined;
|
62
|
+
description?: string | undefined;
|
63
|
+
status?: EntityStatus | undefined;
|
64
|
+
metadata?: Record<string, unknown> | undefined;
|
65
|
+
createdAt?: string | undefined;
|
66
|
+
updatedAt?: string | undefined;
|
67
|
+
}>;
|
68
|
+
export type Entity = z.infer<typeof entitySchema>;
|
69
|
+
/**
|
70
|
+
* Entity-memory link schema definition.
|
71
|
+
*/
|
72
|
+
export declare const entityMemoryLinkSchema: z.ZodObject<{
|
73
|
+
id: z.ZodOptional<z.ZodString>;
|
74
|
+
entityId: z.ZodString;
|
75
|
+
memoryId: z.ZodString;
|
76
|
+
relationship: z.ZodNativeEnum<typeof EntityRelationshipType>;
|
77
|
+
confidence: z.ZodDefault<z.ZodNumber>;
|
78
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
79
|
+
createdAt: z.ZodOptional<z.ZodString>;
|
80
|
+
}, "strip", z.ZodTypeAny, {
|
81
|
+
metadata: Record<string, unknown>;
|
82
|
+
entityId: string;
|
83
|
+
memoryId: string;
|
84
|
+
relationship: EntityRelationshipType;
|
85
|
+
confidence: number;
|
86
|
+
id?: string | undefined;
|
87
|
+
createdAt?: string | undefined;
|
88
|
+
}, {
|
89
|
+
entityId: string;
|
90
|
+
memoryId: string;
|
91
|
+
relationship: EntityRelationshipType;
|
92
|
+
id?: string | undefined;
|
93
|
+
metadata?: Record<string, unknown> | undefined;
|
94
|
+
createdAt?: string | undefined;
|
95
|
+
confidence?: number | undefined;
|
96
|
+
}>;
|
97
|
+
export type EntityMemoryLink = z.infer<typeof entityMemoryLinkSchema>;
|
14
98
|
/**
|
15
99
|
* Subtypes for activity data.
|
16
100
|
*/
|
@@ -216,8 +300,8 @@ export declare const edgeMetadataSchema: z.ZodObject<{
|
|
216
300
|
bidirectional: z.ZodDefault<z.ZodBoolean>;
|
217
301
|
additional: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
218
302
|
}, "strip", z.ZodTypeAny, {
|
219
|
-
additional: Record<string, unknown>;
|
220
303
|
relationship: EdgeType;
|
304
|
+
additional: Record<string, unknown>;
|
221
305
|
weight: number;
|
222
306
|
bidirectional: boolean;
|
223
307
|
validUntil?: Date | undefined;
|
package/dist/core/taxonomy.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.edgeMetadataSchema = exports.memoryMetadataSchema = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = void 0;
|
3
|
+
exports.edgeMetadataSchema = exports.memoryMetadataSchema = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.entityMemoryLinkSchema = exports.entitySchema = exports.EntityStatus = exports.EntityRelationshipType = exports.DataType = void 0;
|
4
4
|
/**
|
5
5
|
* Taxonomy and classification models for MeshOS.
|
6
6
|
*/
|
@@ -15,6 +15,54 @@ var DataType;
|
|
15
15
|
DataType["DECISION"] = "decision";
|
16
16
|
DataType["MEDIA"] = "media";
|
17
17
|
})(DataType || (exports.DataType = DataType = {}));
|
18
|
+
/**
|
19
|
+
* Entity relationship types.
|
20
|
+
*/
|
21
|
+
var EntityRelationshipType;
|
22
|
+
(function (EntityRelationshipType) {
|
23
|
+
EntityRelationshipType["ASSOCIATED"] = "associated";
|
24
|
+
EntityRelationshipType["OWNS"] = "owns";
|
25
|
+
EntityRelationshipType["REFERENCES"] = "references";
|
26
|
+
EntityRelationshipType["MENTIONS"] = "mentions";
|
27
|
+
EntityRelationshipType["DESCRIBES"] = "describes";
|
28
|
+
EntityRelationshipType["PART_OF"] = "part_of";
|
29
|
+
})(EntityRelationshipType || (exports.EntityRelationshipType = EntityRelationshipType = {}));
|
30
|
+
/**
|
31
|
+
* Entity status types.
|
32
|
+
*/
|
33
|
+
var EntityStatus;
|
34
|
+
(function (EntityStatus) {
|
35
|
+
EntityStatus["ACTIVE"] = "active";
|
36
|
+
EntityStatus["INACTIVE"] = "inactive";
|
37
|
+
EntityStatus["ARCHIVED"] = "archived";
|
38
|
+
EntityStatus["DELETED"] = "deleted";
|
39
|
+
})(EntityStatus || (exports.EntityStatus = EntityStatus = {}));
|
40
|
+
/**
|
41
|
+
* Entity schema definition.
|
42
|
+
*/
|
43
|
+
exports.entitySchema = zod_1.z.object({
|
44
|
+
id: zod_1.z.string().uuid().optional(),
|
45
|
+
refId: zod_1.z.string().optional(),
|
46
|
+
type: zod_1.z.string(),
|
47
|
+
name: zod_1.z.string(),
|
48
|
+
description: zod_1.z.string().optional(),
|
49
|
+
metadata: zod_1.z.record(zod_1.z.unknown()).default({}),
|
50
|
+
status: zod_1.z.nativeEnum(EntityStatus).default(EntityStatus.ACTIVE),
|
51
|
+
createdAt: zod_1.z.string().optional(),
|
52
|
+
updatedAt: zod_1.z.string().optional(),
|
53
|
+
});
|
54
|
+
/**
|
55
|
+
* Entity-memory link schema definition.
|
56
|
+
*/
|
57
|
+
exports.entityMemoryLinkSchema = zod_1.z.object({
|
58
|
+
id: zod_1.z.string().uuid().optional(),
|
59
|
+
entityId: zod_1.z.string().uuid(),
|
60
|
+
memoryId: zod_1.z.string().uuid(),
|
61
|
+
relationship: zod_1.z.nativeEnum(EntityRelationshipType),
|
62
|
+
confidence: zod_1.z.number().min(0).max(1).default(1.0),
|
63
|
+
metadata: zod_1.z.record(zod_1.z.unknown()).default({}),
|
64
|
+
createdAt: zod_1.z.string().optional(),
|
65
|
+
});
|
18
66
|
/**
|
19
67
|
* Subtypes for activity data.
|
20
68
|
*/
|
package/package.json
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
-- First drop the constraint since it depends on the function
|
2
|
+
ALTER TABLE public.agents DROP CONSTRAINT IF EXISTS valid_slug;
|
3
|
+
|
4
|
+
-- Now we can safely drop and recreate the function
|
5
|
+
DROP FUNCTION IF EXISTS validate_slug(text);
|
6
|
+
CREATE OR REPLACE FUNCTION validate_slug(slug text)
|
7
|
+
RETURNS boolean AS $$
|
8
|
+
BEGIN
|
9
|
+
RETURN slug ~ '^[a-z][a-z0-9_-]*[a-z0-9]$';
|
10
|
+
END;
|
11
|
+
$$ LANGUAGE plpgsql IMMUTABLE;
|
12
|
+
|
13
|
+
-- Update any existing invalid slugs to NULL
|
14
|
+
UPDATE public.agents
|
15
|
+
SET slug = NULL
|
16
|
+
WHERE slug IS NOT NULL AND NOT validate_slug(slug);
|
17
|
+
|
18
|
+
-- Finally add back the constraint
|
19
|
+
ALTER TABLE public.agents
|
20
|
+
ADD CONSTRAINT valid_slug CHECK (slug IS NULL OR validate_slug(slug));
|
@@ -0,0 +1,13 @@
|
|
1
|
+
-- Drop the combined search function
|
2
|
+
DROP FUNCTION IF EXISTS search_memories_and_entities;
|
3
|
+
|
4
|
+
-- Drop the combined search results view
|
5
|
+
DROP VIEW IF EXISTS public.search_results_with_similarity;
|
6
|
+
|
7
|
+
-- Drop triggers
|
8
|
+
DROP TRIGGER IF EXISTS update_entities_updated_at ON public.entities;
|
9
|
+
DROP TRIGGER IF EXISTS normalize_entity_embedding ON public.entities;
|
10
|
+
|
11
|
+
-- Drop tables (order matters due to foreign key constraints)
|
12
|
+
DROP TABLE IF EXISTS public.entity_memory_links;
|
13
|
+
DROP TABLE IF EXISTS public.entities;
|
@@ -0,0 +1,155 @@
|
|
1
|
+
-- Create entities table with embedding support
|
2
|
+
CREATE TABLE IF NOT EXISTS public.entities (
|
3
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
4
|
+
ref_id TEXT UNIQUE,
|
5
|
+
type TEXT NOT NULL,
|
6
|
+
name TEXT NOT NULL,
|
7
|
+
description TEXT,
|
8
|
+
metadata JSONB,
|
9
|
+
embedding vector(1536),
|
10
|
+
status TEXT NOT NULL DEFAULT 'active',
|
11
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
12
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
13
|
+
);
|
14
|
+
|
15
|
+
-- Create entity-memory links table
|
16
|
+
CREATE TABLE IF NOT EXISTS public.entity_memory_links (
|
17
|
+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
18
|
+
entity_id UUID NOT NULL REFERENCES public.entities(id) ON DELETE CASCADE,
|
19
|
+
memory_id UUID NOT NULL REFERENCES public.memories(id) ON DELETE CASCADE,
|
20
|
+
relationship TEXT NOT NULL,
|
21
|
+
confidence FLOAT DEFAULT 1.0,
|
22
|
+
metadata JSONB,
|
23
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
24
|
+
UNIQUE(entity_id, memory_id, relationship)
|
25
|
+
);
|
26
|
+
|
27
|
+
-- Create index for vector similarity search
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_entities_embedding
|
29
|
+
ON public.entities USING ivfflat (embedding vector_cosine_ops)
|
30
|
+
WITH (lists = 100);
|
31
|
+
|
32
|
+
-- Create trigger for updated_at
|
33
|
+
CREATE TRIGGER update_entities_updated_at
|
34
|
+
BEFORE UPDATE ON public.entities
|
35
|
+
FOR EACH ROW
|
36
|
+
EXECUTE FUNCTION update_updated_at_column();
|
37
|
+
|
38
|
+
-- Add normalization trigger for entities
|
39
|
+
CREATE TRIGGER normalize_entity_embedding
|
40
|
+
BEFORE INSERT OR UPDATE OF embedding ON public.entities
|
41
|
+
FOR EACH ROW
|
42
|
+
EXECUTE FUNCTION normalize_embedding();
|
43
|
+
|
44
|
+
-- Create a view for combined search results
|
45
|
+
CREATE OR REPLACE VIEW public.search_results_with_similarity AS
|
46
|
+
SELECT
|
47
|
+
id,
|
48
|
+
'memory'::text as type,
|
49
|
+
content,
|
50
|
+
metadata,
|
51
|
+
0::float8 as similarity
|
52
|
+
FROM memories
|
53
|
+
UNION ALL
|
54
|
+
SELECT
|
55
|
+
id,
|
56
|
+
'entity'::text as type,
|
57
|
+
name as content,
|
58
|
+
metadata,
|
59
|
+
0::float8 as similarity
|
60
|
+
FROM entities;
|
61
|
+
|
62
|
+
-- Create combined search function
|
63
|
+
CREATE OR REPLACE FUNCTION search_memories_and_entities(
|
64
|
+
query_embedding vector(1536),
|
65
|
+
match_threshold float8,
|
66
|
+
match_count integer,
|
67
|
+
include_entities boolean DEFAULT true,
|
68
|
+
include_memories boolean DEFAULT true,
|
69
|
+
metadata_filter jsonb DEFAULT NULL,
|
70
|
+
created_at_filter jsonb DEFAULT NULL
|
71
|
+
)
|
72
|
+
RETURNS SETOF public.search_results_with_similarity
|
73
|
+
LANGUAGE sql STABLE AS $$
|
74
|
+
WITH normalized_query AS (
|
75
|
+
SELECT l2_normalize(query_embedding) AS normalized_vector
|
76
|
+
)
|
77
|
+
(
|
78
|
+
SELECT
|
79
|
+
m.id,
|
80
|
+
'memory'::text as type,
|
81
|
+
m.content,
|
82
|
+
m.metadata,
|
83
|
+
-(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
|
84
|
+
FROM memories m
|
85
|
+
WHERE include_memories = true
|
86
|
+
AND CASE
|
87
|
+
WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
|
88
|
+
ELSE TRUE
|
89
|
+
END
|
90
|
+
AND CASE
|
91
|
+
WHEN created_at_filter IS NOT NULL THEN (
|
92
|
+
CASE
|
93
|
+
WHEN created_at_filter ? '_gt' THEN m.created_at > (created_at_filter->>'_gt')::timestamptz
|
94
|
+
ELSE TRUE
|
95
|
+
END
|
96
|
+
AND CASE
|
97
|
+
WHEN created_at_filter ? '_gte' THEN m.created_at >= (created_at_filter->>'_gte')::timestamptz
|
98
|
+
ELSE TRUE
|
99
|
+
END
|
100
|
+
AND CASE
|
101
|
+
WHEN created_at_filter ? '_lt' THEN m.created_at < (created_at_filter->>'_lt')::timestamptz
|
102
|
+
ELSE TRUE
|
103
|
+
END
|
104
|
+
AND CASE
|
105
|
+
WHEN created_at_filter ? '_lte' THEN m.created_at <= (created_at_filter->>'_lte')::timestamptz
|
106
|
+
ELSE TRUE
|
107
|
+
END
|
108
|
+
)
|
109
|
+
ELSE TRUE
|
110
|
+
END
|
111
|
+
AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
|
112
|
+
)
|
113
|
+
UNION ALL
|
114
|
+
(
|
115
|
+
SELECT
|
116
|
+
e.id,
|
117
|
+
'entity'::text as type,
|
118
|
+
e.name as content,
|
119
|
+
e.metadata,
|
120
|
+
-(e.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
|
121
|
+
FROM entities e
|
122
|
+
WHERE include_entities = true
|
123
|
+
AND CASE
|
124
|
+
WHEN metadata_filter IS NOT NULL THEN e.metadata @> metadata_filter
|
125
|
+
ELSE TRUE
|
126
|
+
END
|
127
|
+
AND CASE
|
128
|
+
WHEN created_at_filter IS NOT NULL THEN (
|
129
|
+
CASE
|
130
|
+
WHEN created_at_filter ? '_gt' THEN e.created_at > (created_at_filter->>'_gt')::timestamptz
|
131
|
+
ELSE TRUE
|
132
|
+
END
|
133
|
+
AND CASE
|
134
|
+
WHEN created_at_filter ? '_gte' THEN e.created_at >= (created_at_filter->>'_gte')::timestamptz
|
135
|
+
ELSE TRUE
|
136
|
+
END
|
137
|
+
AND CASE
|
138
|
+
WHEN created_at_filter ? '_lt' THEN e.created_at < (created_at_filter->>'_lt')::timestamptz
|
139
|
+
ELSE TRUE
|
140
|
+
END
|
141
|
+
AND CASE
|
142
|
+
WHEN created_at_filter ? '_lte' THEN e.created_at <= (created_at_filter->>'_lte')::timestamptz
|
143
|
+
ELSE TRUE
|
144
|
+
END
|
145
|
+
)
|
146
|
+
ELSE TRUE
|
147
|
+
END
|
148
|
+
AND -(e.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
|
149
|
+
)
|
150
|
+
ORDER BY similarity DESC
|
151
|
+
LIMIT match_count;
|
152
|
+
$$;
|
153
|
+
|
154
|
+
-- Track the function in Hasura
|
155
|
+
COMMENT ON FUNCTION search_memories_and_entities IS E'@graphql({"type": "Query"})';
|