@props-labs/mesh-os 0.1.8 → 0.1.10

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/README.md CHANGED
@@ -143,15 +143,8 @@ flowchart LR
143
143
 
144
144
  ## Getting Started
145
145
 
146
- ### Install & Create a New Instance
147
- ```bash
148
- # Install the CLI tool
149
- npm install -g @props-labs/mesh-os
146
+ > **Important Note**: The JavaScript client can only connect to an existing MeshOS instance. To launch a new MeshOS system, please use the [Python version](https://github.com/Props-Labs/mesh-os) which includes deployment capabilities.
150
147
 
151
- # Create and start a new project
152
- mesh-os init my-project && cd my-project
153
- mesh-os up
154
- ```
155
148
 
156
149
  ## Usage
157
150
  ```typescript
@@ -161,7 +154,7 @@ import dotenv from 'dotenv';
161
154
  // Load environment variables
162
155
  dotenv.config();
163
156
 
164
- // Initialize MeshOS
157
+ // Connect To MeshOS
165
158
  const client = new MeshOS({
166
159
  url: process.env.HASURA_URL || 'http://localhost:8080',
167
160
  apiKey: process.env.HASURA_ADMIN_SECRET || 'meshos',
@@ -1,4 +1,4 @@
1
- import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata } from './taxonomy';
1
+ import { EdgeType, type AgentStatus, type EdgeMetadata, type MemoryMetadata, type TimestampFilter } from './taxonomy';
2
2
  /**
3
3
  * An agent in the system.
4
4
  */
@@ -21,6 +21,7 @@ export interface Memory {
21
21
  embedding: number[];
22
22
  createdAt: string;
23
23
  updatedAt: string;
24
+ expiresAt?: string;
24
25
  similarity?: number;
25
26
  }
26
27
  /**
@@ -91,7 +92,7 @@ export declare class MeshOS {
91
92
  /**
92
93
  * Store a new memory.
93
94
  */
94
- remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>): Promise<Memory>;
95
+ remember(content: string, agentId: string, metadata?: Partial<MemoryMetadata>, expiresAt?: string): Promise<Memory | Memory[]>;
95
96
  /**
96
97
  * Delete a specific memory.
97
98
  */
@@ -107,7 +108,7 @@ export declare class MeshOS {
107
108
  /**
108
109
  * Update a memory and optionally create a version edge to the previous version.
109
110
  */
110
- updateMemory(memoryId: string, content: string, metadata?: Partial<MemoryMetadata>, createVersionEdge?: boolean): Promise<Memory>;
111
+ updateMemory(memoryId: string, content: string, metadata?: Partial<MemoryMetadata>, createVersionEdge?: boolean): Promise<Memory | Memory[]>;
111
112
  /**
112
113
  * Get memories connected to the given memory.
113
114
  */
@@ -134,6 +135,8 @@ export declare class MeshOS {
134
135
  adaptiveThreshold?: boolean;
135
136
  useSemanticExpansion?: boolean;
136
137
  filters?: Record<string, unknown>;
138
+ createdAtFilter?: TimestampFilter;
139
+ expiresAtFilter?: TimestampFilter;
137
140
  }): Promise<Memory[]>;
138
141
  /**
139
142
  * Internal method to perform recall with a specific threshold.
@@ -12,7 +12,6 @@ const chalk_1 = __importDefault(require("chalk"));
12
12
  const boxen_1 = __importDefault(require("boxen"));
13
13
  const taxonomy_1 = require("./taxonomy");
14
14
  // Constants
15
- const MIN_THRESHOLD = 0.3;
16
15
  const SLUG_PATTERN = /^[a-z][a-z0-9_-]*[a-z0-9]$/;
17
16
  /**
18
17
  * GraphQL error class.
@@ -200,7 +199,7 @@ class MeshOS {
200
199
  /**
201
200
  * Store a new memory.
202
201
  */
203
- async remember(content, agentId, metadata) {
202
+ async remember(content, agentId, metadata, expiresAt) {
204
203
  // Create default metadata if none provided
205
204
  const fullMetadata = taxonomy_1.memoryMetadataSchema.parse({
206
205
  type: taxonomy_1.DataType.KNOWLEDGE,
@@ -211,41 +210,128 @@ class MeshOS {
211
210
  additional: {},
212
211
  ...metadata
213
212
  });
214
- // Create embedding
215
- const embedding = await this.createEmbedding(content);
216
- const embeddingStr = `[${embedding.join(',')}]`;
217
- const query = `
218
- mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!) {
219
- insert_memories_one(object: {
220
- content: $content,
221
- agent_id: $agentId,
222
- metadata: $metadata,
223
- embedding: $embedding
224
- }) {
225
- id
226
- agent_id
227
- content
228
- metadata
229
- embedding
230
- created_at
231
- updated_at
213
+ // Check if content needs chunking (8192 tokens is approximately 32K characters)
214
+ const CHUNK_SIZE = 32000;
215
+ if (content.length <= CHUNK_SIZE) {
216
+ // Create embedding for single chunk
217
+ const embedding = await this.createEmbedding(content);
218
+ const embeddingStr = `[${embedding.join(',')}]`;
219
+ const query = `
220
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!, $expiresAt: timestamptz) {
221
+ insert_memories_one(object: {
222
+ content: $content,
223
+ agent_id: $agentId,
224
+ metadata: $metadata,
225
+ embedding: $embedding,
226
+ expires_at: $expiresAt
227
+ }) {
228
+ id
229
+ agent_id
230
+ content
231
+ metadata
232
+ embedding
233
+ created_at
234
+ updated_at
235
+ expires_at
236
+ }
232
237
  }
233
- }
234
- `;
235
- const result = await this.executeQuery(query, {
236
- content,
237
- agentId,
238
- metadata: fullMetadata,
239
- embedding: embeddingStr
240
- });
241
- // Convert snake_case to camelCase
242
- const { agent_id, created_at, updated_at, ...rest } = result.insert_memories_one;
243
- return {
244
- ...rest,
245
- agentId: agent_id,
246
- createdAt: created_at,
247
- updatedAt: updated_at,
248
- };
238
+ `;
239
+ const result = await this.executeQuery(query, {
240
+ content,
241
+ agentId,
242
+ metadata: fullMetadata,
243
+ embedding: embeddingStr,
244
+ expiresAt
245
+ });
246
+ // Convert snake_case to camelCase
247
+ const { agent_id, created_at, updated_at, expires_at, ...rest } = result.insert_memories_one;
248
+ return {
249
+ ...rest,
250
+ agentId: agent_id,
251
+ createdAt: created_at,
252
+ updatedAt: updated_at,
253
+ expiresAt: expires_at,
254
+ };
255
+ }
256
+ // Split content into chunks
257
+ const chunks = [];
258
+ for (let i = 0; i < content.length; i += CHUNK_SIZE) {
259
+ chunks.push(content.slice(i, i + CHUNK_SIZE));
260
+ }
261
+ // Store each chunk and create links
262
+ const memories = [];
263
+ let previousChunkId = null;
264
+ for (let i = 0; i < chunks.length; i++) {
265
+ const chunkMetadata = {
266
+ ...fullMetadata,
267
+ additional: {
268
+ ...fullMetadata.additional,
269
+ chunk_info: {
270
+ chunk_index: i,
271
+ total_chunks: chunks.length,
272
+ is_chunk: true
273
+ }
274
+ }
275
+ };
276
+ // Create embedding for chunk
277
+ const embedding = await this.createEmbedding(chunks[i]);
278
+ const embeddingStr = `[${embedding.join(',')}]`;
279
+ const query = `
280
+ mutation Remember($content: String!, $agentId: uuid!, $metadata: jsonb!, $embedding: vector!, $expiresAt: timestamptz) {
281
+ insert_memories_one(object: {
282
+ content: $content,
283
+ agent_id: $agentId,
284
+ metadata: $metadata,
285
+ embedding: $embedding,
286
+ expires_at: $expiresAt
287
+ }) {
288
+ id
289
+ agent_id
290
+ content
291
+ metadata
292
+ embedding
293
+ created_at
294
+ updated_at
295
+ expires_at
296
+ }
297
+ }
298
+ `;
299
+ const result = await this.executeQuery(query, {
300
+ content: chunks[i],
301
+ agentId,
302
+ metadata: chunkMetadata,
303
+ embedding: embeddingStr,
304
+ expiresAt
305
+ });
306
+ // Convert snake_case to camelCase
307
+ const { agent_id, created_at, updated_at, expires_at, ...rest } = result.insert_memories_one;
308
+ const memory = {
309
+ ...rest,
310
+ agentId: agent_id,
311
+ createdAt: created_at,
312
+ updatedAt: updated_at,
313
+ expiresAt: expires_at,
314
+ };
315
+ memories.push(memory);
316
+ // Link chunks sequentially
317
+ if (previousChunkId) {
318
+ await this.linkMemories(previousChunkId, memory.id, taxonomy_1.EdgeType.FOLLOWS_UP, 1.0, {
319
+ relationship: taxonomy_1.EdgeType.FOLLOWS_UP,
320
+ weight: 1.0,
321
+ bidirectional: false,
322
+ additional: { is_chunk_link: true }
323
+ });
324
+ // Also add PART_OF relationship to show these are parts of the same content
325
+ await this.linkMemories(memory.id, previousChunkId, taxonomy_1.EdgeType.PART_OF, 1.0, {
326
+ relationship: taxonomy_1.EdgeType.PART_OF,
327
+ weight: 1.0,
328
+ bidirectional: true,
329
+ additional: { is_chunk_link: true }
330
+ });
331
+ }
332
+ previousChunkId = memory.id;
333
+ }
334
+ return memories;
249
335
  }
250
336
  /**
251
337
  * Delete a specific memory.
@@ -364,7 +450,7 @@ class MeshOS {
364
450
  throw new Error(`Memory ${memoryId} not found`);
365
451
  }
366
452
  // Create new memory with updated content and metadata
367
- const newMemory = await this.remember(content, oldMemory.agent_id, {
453
+ const newMemories = await this.remember(content, oldMemory.agent_id, {
368
454
  ...oldMemory.metadata,
369
455
  version: oldMemory.metadata.version + 1,
370
456
  previousVersion: oldMemory.id,
@@ -372,14 +458,16 @@ class MeshOS {
372
458
  });
373
459
  // Create version edge if requested
374
460
  if (createVersionEdge) {
375
- await this.linkMemories(oldMemory.id, newMemory.id, taxonomy_1.EdgeType.VERSION_OF, 1.0, {
461
+ // If we got multiple memories (chunks), link the first one
462
+ const firstNewMemory = Array.isArray(newMemories) ? newMemories[0] : newMemories;
463
+ await this.linkMemories(oldMemory.id, firstNewMemory.id, taxonomy_1.EdgeType.VERSION_OF, 1.0, {
376
464
  relationship: taxonomy_1.EdgeType.VERSION_OF,
377
465
  weight: 1.0,
378
466
  bidirectional: false,
379
467
  additional: { version_increment: 1 }
380
468
  });
381
469
  }
382
- return newMemory;
470
+ return newMemories;
383
471
  }
384
472
  /**
385
473
  * Get memories connected to the given memory.
@@ -445,73 +533,86 @@ class MeshOS {
445
533
  * Search memories by semantic similarity.
446
534
  */
447
535
  async recall(options) {
448
- const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters } = options;
536
+ const { query, agentId, limit = 5, threshold = 0.7, minResults = 1, adaptiveThreshold = true, useSemanticExpansion = true, filters, createdAtFilter, expiresAtFilter } = options;
449
537
  // First try: Direct search with initial threshold
450
538
  let results = await this.recallWithThreshold({
451
539
  query,
452
540
  threshold,
453
541
  agentId,
454
542
  limit,
455
- filters
543
+ filters,
544
+ createdAtFilter,
545
+ expiresAtFilter
456
546
  });
457
547
  if (results.length >= minResults) {
458
548
  return results.slice(0, limit);
459
549
  }
460
- // Second try: Adaptive threshold
550
+ // Second try: If adaptive threshold is enabled, try lowering the threshold
461
551
  if (adaptiveThreshold) {
462
552
  let currentThreshold = threshold - 0.05;
463
- while (currentThreshold >= MIN_THRESHOLD && results.length < minResults) {
553
+ const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
554
+ while (currentThreshold >= minThreshold && results.length < minResults) {
464
555
  const newResults = await this.recallWithThreshold({
465
556
  query,
466
557
  threshold: currentThreshold,
467
558
  agentId,
468
559
  limit,
469
- filters
560
+ filters,
561
+ createdAtFilter,
562
+ expiresAtFilter
470
563
  });
471
- // Add new unique results
564
+ // Add new results that aren't already in the list
472
565
  for (const result of newResults) {
473
566
  if (!results.some(r => r.id === result.id)) {
474
567
  results.push(result);
475
568
  }
476
569
  }
477
- if (results.length >= minResults)
570
+ if (results.length >= minResults) {
478
571
  break;
572
+ }
479
573
  currentThreshold -= 0.05;
480
574
  }
481
575
  }
482
- // Third try: Semantic expansion
576
+ // Third try: If we still don't have ANY results and semantic expansion is enabled
483
577
  if (results.length === 0 && useSemanticExpansion) {
484
578
  const variations = await this.expandQuery(query);
485
579
  const seenIds = new Map();
486
- // Try each variation with original threshold
487
- for (const variation of variations.slice(1)) {
580
+ const minThreshold = 0.3; // Don't go below this to avoid irrelevant matches
581
+ // Try each variation with the original threshold first
582
+ for (const variation of variations.slice(1)) { // Skip original query as we already tried it
488
583
  const variationResults = await this.recallWithThreshold({
489
584
  query: variation,
490
585
  threshold,
491
586
  agentId,
492
587
  limit,
493
- filters
588
+ filters,
589
+ createdAtFilter,
590
+ expiresAtFilter
494
591
  });
592
+ // Add new results or update if better similarity
495
593
  for (const memory of variationResults) {
496
594
  const existingMemory = seenIds.get(memory.id);
497
595
  if (!existingMemory || (memory.similarity || 0) > (existingMemory.similarity || 0)) {
498
596
  seenIds.set(memory.id, memory);
499
597
  }
500
598
  }
501
- if (seenIds.size >= minResults)
599
+ if (seenIds.size >= minResults) {
502
600
  break;
601
+ }
503
602
  }
504
603
  // If still no results, try variations with adaptive threshold
505
604
  if (seenIds.size === 0 && adaptiveThreshold) {
506
605
  let currentThreshold = threshold - 0.05;
507
- while (currentThreshold >= MIN_THRESHOLD && seenIds.size === 0) {
606
+ while (currentThreshold >= minThreshold && seenIds.size === 0) {
508
607
  for (const variation of variations.slice(1)) {
509
608
  const variationResults = await this.recallWithThreshold({
510
609
  query: variation,
511
610
  threshold: currentThreshold,
512
611
  agentId,
513
612
  limit,
514
- filters
613
+ filters,
614
+ createdAtFilter,
615
+ expiresAtFilter
515
616
  });
516
617
  for (const memory of variationResults) {
517
618
  const existingMemory = seenIds.get(memory.id);
@@ -519,19 +620,22 @@ class MeshOS {
519
620
  seenIds.set(memory.id, memory);
520
621
  }
521
622
  }
522
- if (seenIds.size > 0)
623
+ if (seenIds.size > 0) {
523
624
  break;
625
+ }
524
626
  }
525
- if (seenIds.size > 0)
627
+ if (seenIds.size > 0) {
526
628
  break;
629
+ }
527
630
  currentThreshold -= 0.05;
528
631
  }
529
632
  }
633
+ // Update results with any found memories
530
634
  if (seenIds.size > 0) {
531
635
  results = Array.from(seenIds.values());
532
636
  }
533
637
  }
534
- // Sort by similarity and return
638
+ // Sort by similarity and return top results
535
639
  results.sort((a, b) => (b.similarity || 0) - (a.similarity || 0));
536
640
  return results.slice(0, limit);
537
641
  }
@@ -539,7 +643,7 @@ class MeshOS {
539
643
  * Internal method to perform recall with a specific threshold.
540
644
  */
541
645
  async recallWithThreshold(options) {
542
- const { query, threshold, agentId, limit = 10, filters } = options;
646
+ const { query, threshold, agentId, limit = 10, filters, createdAtFilter, expiresAtFilter } = options;
543
647
  // Create embedding for the query
544
648
  const embedding = await this.createEmbedding(query);
545
649
  const embeddingStr = `[${embedding.join(',')}]`;
@@ -550,20 +654,48 @@ class MeshOS {
550
654
  agent_id
551
655
  content
552
656
  metadata
553
- embedding
554
657
  similarity
555
658
  created_at
556
659
  updated_at
660
+ expires_at
557
661
  }
558
662
  }
559
663
  `;
664
+ // Process metadata filters
665
+ let metadataFilter;
666
+ if (filters) {
667
+ // Extract metadata filters
668
+ const { type, subtype, tags, relevance, version, _contains, ...additionalFilters } = filters;
669
+ metadataFilter = {};
670
+ // Add basic metadata filters
671
+ if (type)
672
+ metadataFilter.type = type;
673
+ if (subtype)
674
+ metadataFilter.subtype = subtype;
675
+ if (tags)
676
+ metadataFilter.tags = tags;
677
+ if (relevance)
678
+ metadataFilter.relevance = relevance;
679
+ if (version)
680
+ metadataFilter.version = version;
681
+ // Add array containment operations
682
+ if (_contains) {
683
+ metadataFilter._contains = _contains;
684
+ }
685
+ // Add any additional filters to the metadata.additional object
686
+ if (Object.keys(additionalFilters).length > 0) {
687
+ metadataFilter.additional = additionalFilters;
688
+ }
689
+ }
560
690
  const result = await this.executeQuery(gqlQuery, {
561
691
  args: {
562
692
  query_embedding: embeddingStr,
563
693
  match_threshold: threshold,
564
694
  match_count: limit,
565
695
  filter_agent_id: agentId,
566
- ...filters
696
+ metadata_filter: metadataFilter,
697
+ created_at_filter: createdAtFilter,
698
+ expires_at_filter: expiresAtFilter
567
699
  }
568
700
  });
569
701
  return result.search_memories.map(memory => ({
@@ -574,7 +706,8 @@ class MeshOS {
574
706
  embedding: memory.embedding,
575
707
  similarity: memory.similarity,
576
708
  createdAt: memory.created_at,
577
- updatedAt: memory.updated_at
709
+ updatedAt: memory.updated_at,
710
+ expiresAt: memory.expires_at
578
711
  }));
579
712
  }
580
713
  /**
@@ -63,7 +63,8 @@ export declare enum EdgeType {
63
63
  CONTRADICTS = "contradicts",// Conflicting information
64
64
  DEPENDS_ON = "depends_on",// Prerequisite relationship
65
65
  SUMMARIZES = "summarizes",// Condensed version
66
- INFLUENCES = "influences"
66
+ INFLUENCES = "influences",// Impact relationship
67
+ PART_OF = "part_of"
67
68
  }
68
69
  /**
69
70
  * Relevance classification tags.
@@ -228,3 +229,13 @@ export declare const edgeMetadataSchema: z.ZodObject<{
228
229
  bidirectional?: boolean | undefined;
229
230
  }>;
230
231
  export type EdgeMetadata = z.infer<typeof edgeMetadataSchema>;
232
+ /**
233
+ * Timestamp filter operators
234
+ */
235
+ export interface TimestampFilter {
236
+ _gt?: string;
237
+ _gte?: string;
238
+ _lt?: string;
239
+ _lte?: string;
240
+ _eq?: string;
241
+ }
@@ -73,6 +73,7 @@ var EdgeType;
73
73
  EdgeType["DEPENDS_ON"] = "depends_on";
74
74
  EdgeType["SUMMARIZES"] = "summarizes";
75
75
  EdgeType["INFLUENCES"] = "influences";
76
+ EdgeType["PART_OF"] = "part_of";
76
77
  })(EdgeType || (exports.EdgeType = EdgeType = {}));
77
78
  /**
78
79
  * Relevance classification tags.
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@
2
2
  * MeshOS - A lightweight multi-agent memory system with semantic search.
3
3
  */
4
4
  export { Agent, Memory, MemoryEdge, MeshOS, GraphQLError, InvalidSlugError, type MeshOSConfig } from './core/client';
5
- export { DataType, ActivitySubtype, KnowledgeSubtype, DecisionSubtype, MediaSubtype, EdgeType, RelevanceTag, type VersionInfo, type MemoryMetadata, type EdgeMetadata, } from './core/taxonomy';
6
- export declare const VERSION = "0.1.6";
5
+ export { DataType, ActivitySubtype, KnowledgeSubtype, DecisionSubtype, MediaSubtype, EdgeType, RelevanceTag, CoreAgentStatus, type AgentStatus, type VersionInfo, type MemoryMetadata, type EdgeMetadata, } from './core/taxonomy';
6
+ export declare const VERSION = "0.1.9";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * MeshOS - A lightweight multi-agent memory system with semantic search.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.VERSION = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = exports.InvalidSlugError = exports.GraphQLError = exports.MeshOS = void 0;
6
+ exports.VERSION = exports.CoreAgentStatus = exports.RelevanceTag = exports.EdgeType = exports.MediaSubtype = exports.DecisionSubtype = exports.KnowledgeSubtype = exports.ActivitySubtype = exports.DataType = exports.InvalidSlugError = exports.GraphQLError = exports.MeshOS = void 0;
7
7
  var client_1 = require("./core/client");
8
8
  Object.defineProperty(exports, "MeshOS", { enumerable: true, get: function () { return client_1.MeshOS; } });
9
9
  Object.defineProperty(exports, "GraphQLError", { enumerable: true, get: function () { return client_1.GraphQLError; } });
@@ -17,4 +17,6 @@ Object.defineProperty(exports, "DecisionSubtype", { enumerable: true, get: funct
17
17
  Object.defineProperty(exports, "MediaSubtype", { enumerable: true, get: function () { return taxonomy_1.MediaSubtype; } });
18
18
  Object.defineProperty(exports, "EdgeType", { enumerable: true, get: function () { return taxonomy_1.EdgeType; } });
19
19
  Object.defineProperty(exports, "RelevanceTag", { enumerable: true, get: function () { return taxonomy_1.RelevanceTag; } });
20
- exports.VERSION = '0.1.6';
20
+ // Agent status types
21
+ Object.defineProperty(exports, "CoreAgentStatus", { enumerable: true, get: function () { return taxonomy_1.CoreAgentStatus; } });
22
+ exports.VERSION = '0.1.9';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@props-labs/mesh-os",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "MeshOS - A memory system for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,4 +14,10 @@ computed_fields:
14
14
  function:
15
15
  name: search_memories
16
16
  schema: public
17
+ arguments:
18
+ query_embedding: $query_embedding
19
+ match_threshold: $match_threshold
20
+ match_count: $match_count
21
+ filter_agent_id: $filter_agent_id
22
+ metadata_filter: $metadata_filter
17
23
  comment: Computes similarity score for vector search
@@ -1,13 +1,26 @@
1
- -- Drop functions
2
- DROP FUNCTION IF EXISTS search_memories;
1
+ -- Drop all versions of search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid, jsonb);
3
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid);
4
+ DROP FUNCTION IF EXISTS public.search_memories;
5
+
6
+ -- Drop other functions
3
7
  DROP FUNCTION IF EXISTS get_connected_memories;
8
+
9
+ -- Drop triggers first
10
+ DROP TRIGGER IF EXISTS update_agents_updated_at ON public.agents;
11
+ DROP TRIGGER IF EXISTS update_memories_updated_at ON public.memories;
12
+
13
+ -- Now we can safely drop the trigger function
4
14
  DROP FUNCTION IF EXISTS update_updated_at_column;
5
15
 
16
+ -- Drop views first
17
+ DROP VIEW IF EXISTS public.memories_with_similarity;
18
+
6
19
  -- Drop tables
7
20
  DROP TABLE IF EXISTS public.memory_edges;
8
21
  DROP TABLE IF EXISTS public.memories;
9
22
  DROP TABLE IF EXISTS public.agents;
10
23
 
11
- -- Drop extensions
12
- DROP EXTENSION IF EXISTS vector;
24
+ -- Drop extensions (with CASCADE for vector since it has dependent objects)
25
+ DROP EXTENSION IF EXISTS vector CASCADE;
13
26
  DROP EXTENSION IF EXISTS "uuid-ossp";
@@ -0,0 +1,4 @@
1
+ -- Drop the updated search_memories function with metadata filtering
2
+ DROP FUNCTION public.search_memories(vector(1536), float8, integer, uuid, jsonb);
3
+
4
+ -- Note: The original search_memories function from 1_init will be restored when that migration is reapplied
@@ -0,0 +1,44 @@
1
+ -- Drop the existing search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories;
3
+
4
+ -- Create the updated search_memories function with metadata filtering
5
+ CREATE OR REPLACE FUNCTION public.search_memories(
6
+ query_embedding vector(1536),
7
+ match_threshold float8,
8
+ match_count integer,
9
+ filter_agent_id uuid DEFAULT NULL,
10
+ metadata_filter jsonb DEFAULT NULL
11
+ )
12
+ RETURNS SETOF public.memories_with_similarity
13
+ LANGUAGE sql
14
+ STABLE
15
+ AS $$
16
+ WITH normalized_query AS (
17
+ SELECT l2_normalize(query_embedding) AS normalized_vector
18
+ )
19
+ SELECT
20
+ m.id,
21
+ m.agent_id,
22
+ m.content,
23
+ m.metadata,
24
+ m.embedding,
25
+ m.created_at,
26
+ m.updated_at,
27
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
28
+ FROM memories m
29
+ WHERE
30
+ CASE
31
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
32
+ ELSE TRUE
33
+ END
34
+ AND CASE
35
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
36
+ ELSE TRUE
37
+ END
38
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
39
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
40
+ LIMIT match_count;
41
+ $$;
42
+
43
+ -- Track the function in Hasura
44
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';
@@ -0,0 +1,55 @@
1
+ -- Drop the updated search_memories function
2
+ DROP FUNCTION IF EXISTS public.search_memories(vector(1536), float8, integer, uuid, jsonb, jsonb, jsonb);
3
+
4
+ -- Remove expires_at column
5
+ ALTER TABLE public.memories DROP COLUMN expires_at;
6
+
7
+ -- Drop and recreate the view without expires_at
8
+ DROP VIEW IF EXISTS public.memories_with_similarity;
9
+ CREATE OR REPLACE VIEW public.memories_with_similarity AS
10
+ SELECT
11
+ m.*,
12
+ 0::float8 as similarity
13
+ FROM memories m;
14
+
15
+ -- Restore the previous version of search_memories from 2_metadata_filtering
16
+ CREATE OR REPLACE FUNCTION public.search_memories(
17
+ query_embedding vector(1536),
18
+ match_threshold float8,
19
+ match_count integer,
20
+ filter_agent_id uuid DEFAULT NULL,
21
+ metadata_filter jsonb DEFAULT NULL
22
+ )
23
+ RETURNS SETOF public.memories_with_similarity
24
+ LANGUAGE sql
25
+ STABLE
26
+ AS $$
27
+ WITH normalized_query AS (
28
+ SELECT l2_normalize(query_embedding) AS normalized_vector
29
+ )
30
+ SELECT
31
+ m.id,
32
+ m.agent_id,
33
+ m.content,
34
+ m.metadata,
35
+ m.embedding,
36
+ m.created_at,
37
+ m.updated_at,
38
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
39
+ FROM memories m
40
+ WHERE
41
+ CASE
42
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
43
+ ELSE TRUE
44
+ END
45
+ AND CASE
46
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
47
+ ELSE TRUE
48
+ END
49
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
50
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
51
+ LIMIT match_count;
52
+ $$;
53
+
54
+ -- Track the function in Hasura
55
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';
@@ -0,0 +1,108 @@
1
+ -- Add expires_at column to memories table
2
+ ALTER TABLE public.memories ADD COLUMN expires_at TIMESTAMPTZ;
3
+
4
+ -- Create a view for memories with similarity that includes all fields
5
+ DROP VIEW IF EXISTS public.memories_with_similarity;
6
+ CREATE OR REPLACE VIEW public.memories_with_similarity AS
7
+ SELECT
8
+ m.*,
9
+ 0::float8 as similarity -- Default similarity, will be replaced in search
10
+ FROM memories m;
11
+
12
+ -- Drop the existing search_memories function
13
+ DROP FUNCTION IF EXISTS public.search_memories;
14
+
15
+ -- Create the updated search_memories function with standard Hasura filtering
16
+ CREATE OR REPLACE FUNCTION public.search_memories(
17
+ query_embedding vector(1536),
18
+ match_threshold float8,
19
+ match_count integer,
20
+ filter_agent_id uuid DEFAULT NULL,
21
+ metadata_filter jsonb DEFAULT NULL,
22
+ created_at_filter jsonb DEFAULT NULL,
23
+ expires_at_filter jsonb DEFAULT NULL
24
+ )
25
+ RETURNS SETOF public.memories_with_similarity
26
+ LANGUAGE sql
27
+ STABLE
28
+ AS $$
29
+ WITH normalized_query AS (
30
+ SELECT l2_normalize(query_embedding) AS normalized_vector
31
+ )
32
+ SELECT
33
+ m.id,
34
+ m.agent_id,
35
+ m.content,
36
+ m.metadata,
37
+ m.embedding,
38
+ m.created_at,
39
+ m.updated_at,
40
+ m.expires_at,
41
+ -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) as similarity
42
+ FROM memories m
43
+ WHERE
44
+ CASE
45
+ WHEN filter_agent_id IS NOT NULL THEN m.agent_id = filter_agent_id
46
+ ELSE TRUE
47
+ END
48
+ AND CASE
49
+ WHEN metadata_filter IS NOT NULL THEN m.metadata @> metadata_filter
50
+ ELSE TRUE
51
+ END
52
+ AND CASE
53
+ WHEN created_at_filter IS NOT NULL THEN (
54
+ CASE
55
+ WHEN created_at_filter ? '_gt' THEN m.created_at > (created_at_filter->>'_gt')::timestamptz
56
+ ELSE TRUE
57
+ END
58
+ AND CASE
59
+ WHEN created_at_filter ? '_gte' THEN m.created_at >= (created_at_filter->>'_gte')::timestamptz
60
+ ELSE TRUE
61
+ END
62
+ AND CASE
63
+ WHEN created_at_filter ? '_lt' THEN m.created_at < (created_at_filter->>'_lt')::timestamptz
64
+ ELSE TRUE
65
+ END
66
+ AND CASE
67
+ WHEN created_at_filter ? '_lte' THEN m.created_at <= (created_at_filter->>'_lte')::timestamptz
68
+ ELSE TRUE
69
+ END
70
+ AND CASE
71
+ WHEN created_at_filter ? '_eq' THEN m.created_at = (created_at_filter->>'_eq')::timestamptz
72
+ ELSE TRUE
73
+ END
74
+ )
75
+ ELSE TRUE
76
+ END
77
+ AND CASE
78
+ WHEN expires_at_filter IS NOT NULL THEN (
79
+ CASE
80
+ WHEN expires_at_filter ? '_gt' THEN m.expires_at > (expires_at_filter->>'_gt')::timestamptz
81
+ ELSE TRUE
82
+ END
83
+ AND CASE
84
+ WHEN expires_at_filter ? '_gte' THEN m.expires_at >= (expires_at_filter->>'_gte')::timestamptz
85
+ ELSE TRUE
86
+ END
87
+ AND CASE
88
+ WHEN expires_at_filter ? '_lt' THEN m.expires_at < (expires_at_filter->>'_lt')::timestamptz
89
+ ELSE TRUE
90
+ END
91
+ AND CASE
92
+ WHEN expires_at_filter ? '_lte' THEN m.expires_at <= (expires_at_filter->>'_lte')::timestamptz
93
+ ELSE TRUE
94
+ END
95
+ AND CASE
96
+ WHEN expires_at_filter ? '_eq' THEN m.expires_at = (expires_at_filter->>'_eq')::timestamptz
97
+ ELSE TRUE
98
+ END
99
+ )
100
+ ELSE TRUE
101
+ END
102
+ AND -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) >= match_threshold
103
+ ORDER BY -(m.embedding <#> (SELECT normalized_vector FROM normalized_query)) DESC
104
+ LIMIT match_count;
105
+ $$;
106
+
107
+ -- Track the function in Hasura
108
+ COMMENT ON FUNCTION public.search_memories IS E'@graphql({"type": "Query"})';