@props-labs/mesh-os 0.1.8 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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"})';