@axiom-lattice/pg-stores 1.0.34 → 1.0.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/pg-stores",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "PG stores implementation for Axiom Lattice framework",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -21,8 +21,8 @@
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "pg": "^8.16.3",
24
- "@axiom-lattice/protocols": "2.1.23",
25
- "@axiom-lattice/core": "2.1.44"
24
+ "@axiom-lattice/protocols": "2.1.24",
25
+ "@axiom-lattice/core": "2.1.46"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^20.11.24",
@@ -89,6 +89,46 @@ describe('ThreadMessageQueueStore', () => {
89
89
 
90
90
  expect(result.sequence).toBe(5);
91
91
  });
92
+
93
+ it('should persist custom_run_config in insert query', async () => {
94
+ const customRunConfig = {
95
+ modelConfig: {
96
+ modelKey: 'default',
97
+ },
98
+ };
99
+
100
+ mockQuery
101
+ .mockResolvedValueOnce({ rows: [{ next_seq: 1 }] })
102
+ .mockResolvedValueOnce({
103
+ rows: [{
104
+ id: 'msg-1',
105
+ thread_id: mockThreadId,
106
+ tenant_id: mockTenantId,
107
+ assistant_id: mockAssistantId,
108
+ message_content: JSON.stringify('test content'),
109
+ message_type: 'human',
110
+ sequence_order: 1,
111
+ status: 'pending',
112
+ created_at: new Date(),
113
+ custom_run_config: JSON.stringify(customRunConfig),
114
+ }],
115
+ });
116
+
117
+ await store.addMessage({
118
+ threadId: mockThreadId,
119
+ tenantId: mockTenantId,
120
+ assistantId: mockAssistantId,
121
+ content: 'test content',
122
+ type: 'human',
123
+ custom_run_config: customRunConfig,
124
+ });
125
+
126
+ expect(mockQuery).toHaveBeenNthCalledWith(
127
+ 2,
128
+ expect.stringContaining('custom_run_config'),
129
+ expect.arrayContaining([JSON.stringify(customRunConfig)])
130
+ );
131
+ });
92
132
  });
93
133
 
94
134
  describe('addMessageAtHead', () => {
@@ -106,7 +146,13 @@ describe('ThreadMessageQueueStore', () => {
106
146
  }]
107
147
  }); // Insert
108
148
 
109
- const result = await store.addMessageAtHead(mockThreadId, 'head content', 'system');
149
+ const result = await store.addMessageAtHead({
150
+ threadId: mockThreadId,
151
+ tenantId: mockTenantId,
152
+ assistantId: mockAssistantId,
153
+ content: 'head content',
154
+ type: 'system',
155
+ });
110
156
 
111
157
  expect(result.priority).toBe(100);
112
158
  expect(result.sequence).toBe(1);
@@ -127,7 +173,13 @@ describe('ThreadMessageQueueStore', () => {
127
173
  }]
128
174
  });
129
175
 
130
- const result = await store.addMessageAtHead(mockThreadId, 'head content', 'system');
176
+ const result = await store.addMessageAtHead({
177
+ threadId: mockThreadId,
178
+ tenantId: mockTenantId,
179
+ assistantId: mockAssistantId,
180
+ content: 'head content',
181
+ type: 'system',
182
+ });
131
183
 
132
184
  expect(result.priority).toBe(100);
133
185
  });
@@ -175,6 +227,11 @@ describe('ThreadMessageQueueStore', () => {
175
227
  action: 'submit',
176
228
  },
177
229
  };
230
+ const customRunConfig = {
231
+ modelConfig: {
232
+ modelKey: 'default',
233
+ },
234
+ };
178
235
 
179
236
  mockQuery.mockResolvedValueOnce({
180
237
  rows: [
@@ -191,6 +248,7 @@ describe('ThreadMessageQueueStore', () => {
191
248
  },
192
249
  }),
193
250
  command: JSON.stringify(command),
251
+ custom_run_config: JSON.stringify(customRunConfig),
194
252
  message_type: 'human',
195
253
  sequence_order: 1,
196
254
  created_at: new Date('2024-01-01'),
@@ -202,6 +260,7 @@ describe('ThreadMessageQueueStore', () => {
202
260
  const result = await store.getPendingMessages(mockThreadId);
203
261
 
204
262
  expect(result[0].command).toEqual(command);
263
+ expect(result[0].custom_run_config).toEqual(customRunConfig);
205
264
  expect((result[0].content as { command?: unknown }).command).toBeUndefined();
206
265
  });
207
266
  });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Add custom_run_config column to thread message queue
3
+ */
4
+
5
+ import { PoolClient } from "pg";
6
+ import { Migration } from "./migration";
7
+
8
+ /**
9
+ * Migration: Add custom_run_config column
10
+ */
11
+ export const addCustomRunConfigColumn: Migration = {
12
+ version: 102,
13
+ name: "add_custom_run_config_column",
14
+ up: async (client: PoolClient) => {
15
+ await client.query(`
16
+ ALTER TABLE lattice_thread_message_queue
17
+ ADD COLUMN IF NOT EXISTS custom_run_config JSONB
18
+ `);
19
+ },
20
+ down: async (client: PoolClient) => {
21
+ await client.query(
22
+ "ALTER TABLE lattice_thread_message_queue DROP COLUMN IF EXISTS custom_run_config"
23
+ );
24
+ },
25
+ };
@@ -103,7 +103,11 @@ export class PostgreSQLThreadStore implements ThreadStore {
103
103
  /**
104
104
  * Get all threads for a specific tenant and assistant
105
105
  */
106
- async getThreadsByAssistantId(tenantId: string, assistantId: string): Promise<Thread[]> {
106
+ async getThreadsByAssistantId(
107
+ tenantId: string,
108
+ assistantId: string,
109
+ metadataFilter?: Record<string, string>
110
+ ): Promise<Thread[]> {
107
111
  await this.ensureInitialized();
108
112
 
109
113
  const result = await this.pool.query<{
@@ -123,7 +127,18 @@ export class PostgreSQLThreadStore implements ThreadStore {
123
127
  [tenantId, assistantId]
124
128
  );
125
129
 
126
- return result.rows.map(this.mapRowToThread);
130
+ let threads = result.rows.map(this.mapRowToThread);
131
+
132
+ // Apply metadata filtering if provided
133
+ if (metadataFilter && Object.keys(metadataFilter).length > 0) {
134
+ threads = threads.filter((thread) =>
135
+ Object.entries(metadataFilter).every(
136
+ ([key, value]) => thread.metadata?.[key] === value
137
+ )
138
+ );
139
+ }
140
+
141
+ return threads;
127
142
  }
128
143
 
129
144
  /**
@@ -15,6 +15,7 @@ import {
15
15
  import { MigrationManager } from "../migrations/migration";
16
16
  import { createThreadMessageQueueTable } from "../migrations/thread_message_queue_migrations";
17
17
  import { addPriorityAndCommandColumns } from "../migrations/add_priority_command_columns";
18
+ import { addCustomRunConfigColumn } from "../migrations/add_custom_run_config_column";
18
19
  import { alterMessageQueueIdColumn } from "../migrations/alter_message_queue_id_column";
19
20
 
20
21
  export { PendingMessage, AddMessageParams, ThreadInfo };
@@ -45,6 +46,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
45
46
  const migrationManager = new MigrationManager(pool);
46
47
  migrationManager.register(createThreadMessageQueueTable);
47
48
  migrationManager.register(addPriorityAndCommandColumns);
49
+ migrationManager.register(addCustomRunConfigColumn);
48
50
  migrationManager.register(alterMessageQueueIdColumn);
49
51
  await migrationManager.migrate();
50
52
  this.initialized = true;
@@ -63,7 +65,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
63
65
  */
64
66
  async addMessage(params: AddMessageParams): Promise<PendingMessage> {
65
67
  const pool = this.getPool();
66
- const { threadId, tenantId, assistantId, content, type = "human", priority = 0, command, id } = params;
68
+ const { threadId, tenantId, assistantId, content, type = "human", priority = 0, command, custom_run_config, id } = params;
67
69
 
68
70
  // Get current max sequence (no lock needed without unique constraint)
69
71
  const seqResult = await pool.query(
@@ -76,10 +78,10 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
76
78
 
77
79
  const result = await pool.query(
78
80
  `INSERT INTO lattice_thread_message_queue
79
- (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command)
80
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
81
+ (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
82
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
81
83
  RETURNING *`,
82
- [id || crypto.randomUUID(), threadId, tenantId, assistantId, JSON.stringify(content), type, nextSeq, priority, command ? JSON.stringify(command) : null]
84
+ [id || crypto.randomUUID(), threadId, tenantId, assistantId, JSON.stringify(content), type, nextSeq, priority, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
83
85
  );
84
86
 
85
87
  return this.rowToMessage(result.rows[0]);
@@ -89,31 +91,14 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
89
91
  * Add message at head of queue (high priority, e.g., STEER/Command messages)
90
92
  * Uses priority=100 to ensure message is processed first
91
93
  */
92
- async addMessageAtHead(
93
- threadId: string,
94
- content: PendingMessage["content"],
95
- type: "human" | "system" = "system",
96
- id?: string,
97
- command?: PendingMessage["command"]
98
- ): Promise<PendingMessage> {
94
+ async addMessageAtHead(params: AddMessageParams): Promise<PendingMessage> {
99
95
  const pool = this.getPool();
96
+ const { threadId, tenantId, assistantId, content, type = "human", command, custom_run_config, id } = params;
100
97
 
101
- // Get thread info from existing messages
102
- const threadResult = await pool.query(
103
- `SELECT tenant_id, assistant_id
104
- FROM lattice_thread_message_queue
105
- WHERE thread_id = $1
106
- LIMIT 1`,
107
- [threadId]
108
- );
109
-
110
- let tenantId = "default";
111
- let assistantId = "";
112
-
113
- if (threadResult.rows.length > 0) {
114
- tenantId = threadResult.rows[0].tenant_id;
115
- assistantId = threadResult.rows[0].assistant_id;
116
- }
98
+ // Use provided tenantId and assistantId directly
99
+ // These should always be provided by the caller (Agent)
100
+ const resolvedTenantId = tenantId!;
101
+ const resolvedAssistantId = assistantId!;
117
102
 
118
103
  // Get current max sequence for ordering within same priority
119
104
  const seqResult = await pool.query(
@@ -127,10 +112,10 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
127
112
  // Insert with high priority (100)
128
113
  const result = await pool.query(
129
114
  `INSERT INTO lattice_thread_message_queue
130
- (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command)
131
- VALUES ($1, $2, $3, $4, $5, $6, $7, 100, $8)
115
+ (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
116
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 100, $8, $9)
132
117
  RETURNING *`,
133
- [id || crypto.randomUUID(), threadId, tenantId, assistantId, JSON.stringify(content), type, nextSeq, command ? JSON.stringify(command) : null]
118
+ [id || crypto.randomUUID(), threadId, resolvedTenantId, resolvedAssistantId, JSON.stringify(content), type, nextSeq, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
134
119
  );
135
120
 
136
121
  return this.rowToMessage(result.rows[0]);
@@ -284,6 +269,9 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
284
269
  createdAt: new Date(row.created_at),
285
270
  priority: row.priority || 0,
286
271
  command: row.command ? (typeof row.command === "string" ? JSON.parse(row.command) : row.command) : undefined,
272
+ custom_run_config: row.custom_run_config
273
+ ? (typeof row.custom_run_config === "string" ? JSON.parse(row.custom_run_config) : row.custom_run_config)
274
+ : undefined,
287
275
  };
288
276
  }
289
277
  }