graphile-llm 1.11.1 → 1.11.3
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.
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Results are cached per-database with a TTL so the REST middleware
|
|
9
9
|
* doesn't hit the database on every request.
|
|
10
|
+
*
|
|
11
|
+
* Discovery is keyed by `database_id`, as every other module lookup is: one
|
|
12
|
+
* serving database holds several tenants' schemas, so an unkeyed lookup does
|
|
13
|
+
* not fail — it resolves a neighbouring tenant's agent tables, and the cache
|
|
14
|
+
* then serves that for its whole TTL.
|
|
10
15
|
*/
|
|
11
16
|
import { Pool } from 'pg';
|
|
12
17
|
export interface AgentTableInfo {
|
|
@@ -24,6 +29,6 @@ export interface AgentDiscovery {
|
|
|
24
29
|
export declare function clearAgentDiscoveryCache(): void;
|
|
25
30
|
/**
|
|
26
31
|
* Look up agent table info for a database, querying the module config table.
|
|
27
|
-
* Results are cached per
|
|
32
|
+
* Results are cached per database id with a 60s TTL.
|
|
28
33
|
*/
|
|
29
|
-
export declare function getAgentDiscovery(pool: Pool,
|
|
34
|
+
export declare function getAgentDiscovery(pool: Pool, databaseId: string): Promise<AgentDiscovery | null>;
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Results are cached per-database with a TTL so the REST middleware
|
|
9
9
|
* doesn't hit the database on every request.
|
|
10
|
+
*
|
|
11
|
+
* Discovery is keyed by `database_id`, as every other module lookup is: one
|
|
12
|
+
* serving database holds several tenants' schemas, so an unkeyed lookup does
|
|
13
|
+
* not fail — it resolves a neighbouring tenant's agent tables, and the cache
|
|
14
|
+
* then serves that for its whole TTL.
|
|
10
15
|
*/
|
|
11
16
|
import { ModuleConfigCache } from 'graphile-cache';
|
|
12
17
|
// ─── Cache ──────────────────────────────────────────────────────────────────
|
|
@@ -27,20 +32,34 @@ const DISCOVERY_SQL = `
|
|
|
27
32
|
acm.task_table_name
|
|
28
33
|
FROM metaschema_modules_public.agent_chat_module acm
|
|
29
34
|
JOIN metaschema_public.schema s ON s.id = acm.schema_id
|
|
35
|
+
WHERE acm.database_id = $1
|
|
30
36
|
LIMIT 1
|
|
31
37
|
`;
|
|
38
|
+
/** The module (or the whole metaschema) is simply absent from this database. */
|
|
39
|
+
const NOT_PROVISIONED = new Set([
|
|
40
|
+
'42P01', // undefined_table
|
|
41
|
+
'3F000' // invalid_schema_name
|
|
42
|
+
]);
|
|
43
|
+
const isNotProvisioned = (err) => typeof err === 'object' &&
|
|
44
|
+
err !== null &&
|
|
45
|
+
'code' in err &&
|
|
46
|
+
typeof err.code === 'string' &&
|
|
47
|
+
NOT_PROVISIONED.has(err.code);
|
|
32
48
|
/**
|
|
33
49
|
* Look up agent table info for a database, querying the module config table.
|
|
34
|
-
* Results are cached per
|
|
50
|
+
* Results are cached per database id with a 60s TTL.
|
|
35
51
|
*/
|
|
36
|
-
export async function getAgentDiscovery(pool,
|
|
37
|
-
|
|
52
|
+
export async function getAgentDiscovery(pool, databaseId) {
|
|
53
|
+
if (!databaseId) {
|
|
54
|
+
throw new Error('getAgentDiscovery: databaseId is required');
|
|
55
|
+
}
|
|
56
|
+
const cached = agentDiscoveryCache.get(databaseId);
|
|
38
57
|
if (cached !== undefined) {
|
|
39
58
|
return cached;
|
|
40
59
|
}
|
|
41
60
|
let discovery = null;
|
|
42
61
|
try {
|
|
43
|
-
const { rows } = await pool.query(DISCOVERY_SQL);
|
|
62
|
+
const { rows } = await pool.query(DISCOVERY_SQL, [databaseId]);
|
|
44
63
|
if (rows.length > 0) {
|
|
45
64
|
const row = rows[0];
|
|
46
65
|
const schemaName = row.schema_name;
|
|
@@ -57,9 +76,12 @@ export async function getAgentDiscovery(pool, dbname) {
|
|
|
57
76
|
};
|
|
58
77
|
}
|
|
59
78
|
}
|
|
60
|
-
catch {
|
|
61
|
-
//
|
|
79
|
+
catch (err) {
|
|
80
|
+
// Only the absence being probed for is swallowed. A dead pool or a bad
|
|
81
|
+
// databaseId reported as "not provisioned" is a silently agent-less API.
|
|
82
|
+
if (!isNotProvisioned(err))
|
|
83
|
+
throw err;
|
|
62
84
|
}
|
|
63
|
-
agentDiscoveryCache.set(
|
|
85
|
+
agentDiscoveryCache.set(databaseId, discovery);
|
|
64
86
|
return discovery;
|
|
65
87
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-llm",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.3",
|
|
4
4
|
"description": "LLM integration plugin for PostGraphile v5 — server-side text-to-vector embedding and text companion fields for pgvector columns",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@agentic-kit/ollama": "2.13.0",
|
|
33
|
-
"@constructive-io/express-context": "^0.24.
|
|
33
|
+
"@constructive-io/express-context": "^0.24.2",
|
|
34
34
|
"@constructive-io/llm-env": "^0.13.0",
|
|
35
|
-
"graphile-cache": "^4.10.
|
|
35
|
+
"graphile-cache": "^4.10.2"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@dataplan/pg": "1.0.3",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^22.19.11",
|
|
59
|
-
"graphile-connection-filter": "^2.11.
|
|
60
|
-
"graphile-search": "^2.11.
|
|
61
|
-
"graphile-test": "^5.11.
|
|
59
|
+
"graphile-connection-filter": "^2.11.3",
|
|
60
|
+
"graphile-search": "^2.11.3",
|
|
61
|
+
"graphile-test": "^5.11.3",
|
|
62
62
|
"makage": "^0.3.0",
|
|
63
|
-
"pgsql-test": "^5.10.
|
|
63
|
+
"pgsql-test": "^5.10.3"
|
|
64
64
|
},
|
|
65
65
|
"keywords": [
|
|
66
66
|
"postgraphile",
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"ollama",
|
|
76
76
|
"openai"
|
|
77
77
|
],
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "43121004a8bedd2c5fcf7bf33208079d55bad01f"
|
|
79
79
|
}
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Results are cached per-database with a TTL so the REST middleware
|
|
9
9
|
* doesn't hit the database on every request.
|
|
10
|
+
*
|
|
11
|
+
* Discovery is keyed by `database_id`, as every other module lookup is: one
|
|
12
|
+
* serving database holds several tenants' schemas, so an unkeyed lookup does
|
|
13
|
+
* not fail — it resolves a neighbouring tenant's agent tables, and the cache
|
|
14
|
+
* then serves that for its whole TTL.
|
|
10
15
|
*/
|
|
11
16
|
import { Pool } from 'pg';
|
|
12
17
|
export interface AgentTableInfo {
|
|
@@ -24,6 +29,6 @@ export interface AgentDiscovery {
|
|
|
24
29
|
export declare function clearAgentDiscoveryCache(): void;
|
|
25
30
|
/**
|
|
26
31
|
* Look up agent table info for a database, querying the module config table.
|
|
27
|
-
* Results are cached per
|
|
32
|
+
* Results are cached per database id with a 60s TTL.
|
|
28
33
|
*/
|
|
29
|
-
export declare function getAgentDiscovery(pool: Pool,
|
|
34
|
+
export declare function getAgentDiscovery(pool: Pool, databaseId: string): Promise<AgentDiscovery | null>;
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Results are cached per-database with a TTL so the REST middleware
|
|
10
10
|
* doesn't hit the database on every request.
|
|
11
|
+
*
|
|
12
|
+
* Discovery is keyed by `database_id`, as every other module lookup is: one
|
|
13
|
+
* serving database holds several tenants' schemas, so an unkeyed lookup does
|
|
14
|
+
* not fail — it resolves a neighbouring tenant's agent tables, and the cache
|
|
15
|
+
* then serves that for its whole TTL.
|
|
11
16
|
*/
|
|
12
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
18
|
exports.clearAgentDiscoveryCache = clearAgentDiscoveryCache;
|
|
@@ -31,20 +36,34 @@ const DISCOVERY_SQL = `
|
|
|
31
36
|
acm.task_table_name
|
|
32
37
|
FROM metaschema_modules_public.agent_chat_module acm
|
|
33
38
|
JOIN metaschema_public.schema s ON s.id = acm.schema_id
|
|
39
|
+
WHERE acm.database_id = $1
|
|
34
40
|
LIMIT 1
|
|
35
41
|
`;
|
|
42
|
+
/** The module (or the whole metaschema) is simply absent from this database. */
|
|
43
|
+
const NOT_PROVISIONED = new Set([
|
|
44
|
+
'42P01', // undefined_table
|
|
45
|
+
'3F000' // invalid_schema_name
|
|
46
|
+
]);
|
|
47
|
+
const isNotProvisioned = (err) => typeof err === 'object' &&
|
|
48
|
+
err !== null &&
|
|
49
|
+
'code' in err &&
|
|
50
|
+
typeof err.code === 'string' &&
|
|
51
|
+
NOT_PROVISIONED.has(err.code);
|
|
36
52
|
/**
|
|
37
53
|
* Look up agent table info for a database, querying the module config table.
|
|
38
|
-
* Results are cached per
|
|
54
|
+
* Results are cached per database id with a 60s TTL.
|
|
39
55
|
*/
|
|
40
|
-
async function getAgentDiscovery(pool,
|
|
41
|
-
|
|
56
|
+
async function getAgentDiscovery(pool, databaseId) {
|
|
57
|
+
if (!databaseId) {
|
|
58
|
+
throw new Error('getAgentDiscovery: databaseId is required');
|
|
59
|
+
}
|
|
60
|
+
const cached = agentDiscoveryCache.get(databaseId);
|
|
42
61
|
if (cached !== undefined) {
|
|
43
62
|
return cached;
|
|
44
63
|
}
|
|
45
64
|
let discovery = null;
|
|
46
65
|
try {
|
|
47
|
-
const { rows } = await pool.query(DISCOVERY_SQL);
|
|
66
|
+
const { rows } = await pool.query(DISCOVERY_SQL, [databaseId]);
|
|
48
67
|
if (rows.length > 0) {
|
|
49
68
|
const row = rows[0];
|
|
50
69
|
const schemaName = row.schema_name;
|
|
@@ -61,9 +80,12 @@ async function getAgentDiscovery(pool, dbname) {
|
|
|
61
80
|
};
|
|
62
81
|
}
|
|
63
82
|
}
|
|
64
|
-
catch {
|
|
65
|
-
//
|
|
83
|
+
catch (err) {
|
|
84
|
+
// Only the absence being probed for is swallowed. A dead pool or a bad
|
|
85
|
+
// databaseId reported as "not provisioned" is a silently agent-less API.
|
|
86
|
+
if (!isNotProvisioned(err))
|
|
87
|
+
throw err;
|
|
66
88
|
}
|
|
67
|
-
agentDiscoveryCache.set(
|
|
89
|
+
agentDiscoveryCache.set(databaseId, discovery);
|
|
68
90
|
return discovery;
|
|
69
91
|
}
|