graphile-llm 0.7.3 → 0.9.0
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/__tests__/graphile-llm.test.js +87 -71
- package/chat.d.ts +5 -5
- package/chat.js +45 -43
- package/config-cache.d.ts +77 -0
- package/config-cache.js +148 -0
- package/embedder.d.ts +5 -5
- package/embedder.js +11 -17
- package/env.d.ts +31 -0
- package/env.js +52 -0
- package/esm/__tests__/graphile-llm.test.js +87 -71
- package/esm/chat.d.ts +5 -5
- package/esm/chat.js +45 -40
- package/esm/config-cache.d.ts +77 -0
- package/esm/config-cache.js +143 -0
- package/esm/embedder.d.ts +5 -5
- package/esm/embedder.js +11 -17
- package/esm/env.d.ts +31 -0
- package/esm/env.js +49 -0
- package/esm/index.d.ts +14 -5
- package/esm/index.js +11 -5
- package/esm/metering.d.ts +114 -0
- package/esm/metering.js +352 -0
- package/esm/plugins/agent-discovery-plugin.d.ts +29 -0
- package/esm/plugins/agent-discovery-plugin.js +65 -0
- package/esm/plugins/llm-module-plugin.d.ts +11 -2
- package/esm/plugins/llm-module-plugin.js +15 -7
- package/esm/plugins/metering-plugin.d.ts +42 -0
- package/esm/plugins/metering-plugin.js +175 -0
- package/esm/plugins/rag-plugin.js +20 -20
- package/esm/plugins/text-mutation-plugin.d.ts +4 -0
- package/esm/plugins/text-mutation-plugin.js +23 -13
- package/esm/plugins/text-search-plugin.d.ts +4 -0
- package/esm/plugins/text-search-plugin.js +23 -11
- package/esm/preset.d.ts +21 -1
- package/esm/preset.js +33 -6
- package/esm/types.d.ts +86 -10
- package/index.d.ts +14 -5
- package/index.js +25 -8
- package/metering.d.ts +114 -0
- package/metering.js +359 -0
- package/package.json +15 -15
- package/plugins/agent-discovery-plugin.d.ts +29 -0
- package/plugins/agent-discovery-plugin.js +69 -0
- package/plugins/llm-module-plugin.d.ts +11 -2
- package/plugins/llm-module-plugin.js +15 -7
- package/plugins/metering-plugin.d.ts +42 -0
- package/plugins/metering-plugin.js +178 -0
- package/plugins/rag-plugin.js +20 -20
- package/plugins/text-mutation-plugin.d.ts +4 -0
- package/plugins/text-mutation-plugin.js +23 -13
- package/plugins/text-search-plugin.d.ts +4 -0
- package/plugins/text-search-plugin.js +23 -11
- package/preset.d.ts +21 -1
- package/preset.js +33 -6
- package/types.d.ts +86 -10
package/config-cache.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* config-cache — Per-database LLM billing configuration cache
|
|
4
|
+
*
|
|
5
|
+
* Caches resolved billing function names per database_id.
|
|
6
|
+
* Uses an LRU cache with TTL so config changes propagate within a bounded window
|
|
7
|
+
* without requiring a server restart.
|
|
8
|
+
*
|
|
9
|
+
* Resolution flow:
|
|
10
|
+
* Billing config from `metaschema_modules_public.billing_module`
|
|
11
|
+
* (schema name + function names for record_usage, check_billing_quota)
|
|
12
|
+
*
|
|
13
|
+
* All queries run through the Graphile `withPgClient` callback, which gives us
|
|
14
|
+
* a client connected to the tenant database with proper role settings.
|
|
15
|
+
*
|
|
16
|
+
* The LLM module config (provider, model, etc.) is already resolved by the
|
|
17
|
+
* LlmModulePlugin at schema-build time. This cache handles the runtime-only
|
|
18
|
+
* billing piece.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.getLlmBillingConfig = getLlmBillingConfig;
|
|
22
|
+
exports.invalidateLlmBillingConfig = invalidateLlmBillingConfig;
|
|
23
|
+
exports.getLlmBillingCacheStats = getLlmBillingCacheStats;
|
|
24
|
+
const graphile_cache_1 = require("graphile-cache");
|
|
25
|
+
// ─── SQL Queries ────────────────────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Check if the billing_module table exists before querying it.
|
|
28
|
+
* This prevents hard errors on databases that don't have the billing
|
|
29
|
+
* module provisioned (the metaschema_modules_public schema or the
|
|
30
|
+
* billing_module table might not exist at all).
|
|
31
|
+
*/
|
|
32
|
+
const BILLING_MODULE_SQL = `
|
|
33
|
+
SELECT
|
|
34
|
+
s.schema_name AS public_schema,
|
|
35
|
+
ps.schema_name AS private_schema,
|
|
36
|
+
bm.record_usage_function
|
|
37
|
+
FROM metaschema_modules_public.billing_module bm
|
|
38
|
+
JOIN metaschema_public.schema s ON bm.schema_id = s.id
|
|
39
|
+
JOIN metaschema_public.schema ps ON bm.private_schema_id = ps.id
|
|
40
|
+
WHERE bm.database_id = $1
|
|
41
|
+
LIMIT 1
|
|
42
|
+
`;
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the inference log module's schema and table name.
|
|
45
|
+
*/
|
|
46
|
+
const INFERENCE_LOG_MODULE_SQL = `
|
|
47
|
+
SELECT
|
|
48
|
+
s.schema_name AS schema,
|
|
49
|
+
ilm.inference_log_table_name AS table_name
|
|
50
|
+
FROM metaschema_modules_public.inference_log_module ilm
|
|
51
|
+
JOIN metaschema_public.schema s ON ilm.schema_id = s.id
|
|
52
|
+
WHERE ilm.database_id = $1
|
|
53
|
+
LIMIT 1
|
|
54
|
+
`;
|
|
55
|
+
// ─── Cache ──────────────────────────────────────────────────────────────────
|
|
56
|
+
const billingCache = new graphile_cache_1.ModuleConfigCache({
|
|
57
|
+
name: 'billing-config',
|
|
58
|
+
ttlMs: 5 * 60 * 1000, // 5 minutes
|
|
59
|
+
max: 50
|
|
60
|
+
});
|
|
61
|
+
// ─── Resolution Functions ───────────────────────────────────────────────────
|
|
62
|
+
/**
|
|
63
|
+
* SQL to check if a schema exists. Used as a guard before querying
|
|
64
|
+
* metaschema tables that may not be provisioned.
|
|
65
|
+
*/
|
|
66
|
+
const SCHEMA_EXISTS_SQL = `
|
|
67
|
+
SELECT 1 FROM information_schema.schemata WHERE schema_name = $1 LIMIT 1
|
|
68
|
+
`;
|
|
69
|
+
async function resolveInferenceLogConfig(pgClient, databaseId) {
|
|
70
|
+
try {
|
|
71
|
+
const schemaCheck = await pgClient.query(SCHEMA_EXISTS_SQL, ['metaschema_modules_public']);
|
|
72
|
+
if (schemaCheck.rows.length === 0)
|
|
73
|
+
return null;
|
|
74
|
+
const result = await pgClient.query(INFERENCE_LOG_MODULE_SQL, [databaseId]);
|
|
75
|
+
const row = result.rows[0];
|
|
76
|
+
if (!row?.schema || !row?.table_name)
|
|
77
|
+
return null;
|
|
78
|
+
return {
|
|
79
|
+
schema: row.schema,
|
|
80
|
+
tableName: row.table_name
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function resolveBillingConfig(pgClient, databaseId) {
|
|
88
|
+
try {
|
|
89
|
+
// Guard: check if the metaschema_modules_public schema exists.
|
|
90
|
+
// If the database doesn't have the billing module provisioned,
|
|
91
|
+
// this schema (or the billing_module table) won't exist.
|
|
92
|
+
const schemaCheck = await pgClient.query(SCHEMA_EXISTS_SQL, ['metaschema_modules_public']);
|
|
93
|
+
if (schemaCheck.rows.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
const result = await pgClient.query(BILLING_MODULE_SQL, [databaseId]);
|
|
96
|
+
const row = result.rows[0];
|
|
97
|
+
if (!row?.record_usage_function)
|
|
98
|
+
return null;
|
|
99
|
+
return {
|
|
100
|
+
publicSchema: row.public_schema,
|
|
101
|
+
privateSchema: row.private_schema,
|
|
102
|
+
recordUsageFunction: row.record_usage_function,
|
|
103
|
+
// The check_billing_quota function name follows the inflection pattern
|
|
104
|
+
checkBillingQuotaFunction: 'check_billing_quota'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Schema/table doesn't exist or query failed — billing not available
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
113
|
+
/**
|
|
114
|
+
* Resolve billing config for a database.
|
|
115
|
+
* Results are cached per database_id with a 5-minute TTL.
|
|
116
|
+
*
|
|
117
|
+
* @param pgClient - A client connected to the tenant database (from withPgClient)
|
|
118
|
+
* @param databaseId - The database UUID
|
|
119
|
+
*/
|
|
120
|
+
async function getLlmBillingConfig(pgClient, databaseId) {
|
|
121
|
+
const cached = billingCache.get(databaseId);
|
|
122
|
+
if (cached)
|
|
123
|
+
return cached;
|
|
124
|
+
const [billing, inferenceLog] = await Promise.all([
|
|
125
|
+
resolveBillingConfig(pgClient, databaseId),
|
|
126
|
+
resolveInferenceLogConfig(pgClient, databaseId)
|
|
127
|
+
]);
|
|
128
|
+
const entry = { billing, inferenceLog };
|
|
129
|
+
billingCache.set(databaseId, entry);
|
|
130
|
+
return entry;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Invalidate the cached config for a specific database (or all).
|
|
134
|
+
*/
|
|
135
|
+
function invalidateLlmBillingConfig(databaseId) {
|
|
136
|
+
if (databaseId) {
|
|
137
|
+
billingCache.delete(databaseId);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
billingCache.clear();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get cache stats for diagnostics.
|
|
145
|
+
*/
|
|
146
|
+
function getLlmBillingCacheStats() {
|
|
147
|
+
return { size: billingCache.size, max: 50 };
|
|
148
|
+
}
|
package/embedder.d.ts
CHANGED
|
@@ -24,12 +24,12 @@ export declare function buildEmbedder(config: EmbedderConfig): EmbedderFunction
|
|
|
24
24
|
*/
|
|
25
25
|
export declare function buildEmbedderFromModule(data: LlmModuleData): EmbedderFunction | null;
|
|
26
26
|
/**
|
|
27
|
-
* Resolve an embedder from environment variables
|
|
27
|
+
* Resolve an embedder from environment variables.
|
|
28
28
|
* This is a fallback for development when no llm_module or defaultEmbedder is configured.
|
|
29
29
|
*
|
|
30
|
-
* Environment variables (
|
|
31
|
-
* EMBEDDER_PROVIDER
|
|
32
|
-
* EMBEDDER_MODEL
|
|
33
|
-
* EMBEDDER_BASE_URL
|
|
30
|
+
* Environment variables (with defaults from env.ts):
|
|
31
|
+
* EMBEDDER_PROVIDER - Provider name (default: 'ollama')
|
|
32
|
+
* EMBEDDER_MODEL - Model identifier (default: 'nomic-embed-text')
|
|
33
|
+
* EMBEDDER_BASE_URL - Provider base URL (default: 'http://localhost:11434')
|
|
34
34
|
*/
|
|
35
35
|
export declare function buildEmbedderFromEnv(): EmbedderFunction | null;
|
package/embedder.js
CHANGED
|
@@ -18,10 +18,12 @@ exports.buildEmbedder = buildEmbedder;
|
|
|
18
18
|
exports.buildEmbedderFromModule = buildEmbedderFromModule;
|
|
19
19
|
exports.buildEmbedderFromEnv = buildEmbedderFromEnv;
|
|
20
20
|
const ollama_1 = __importDefault(require("@agentic-kit/ollama"));
|
|
21
|
-
const
|
|
21
|
+
const env_1 = require("./env");
|
|
22
22
|
// ─── Built-in Providers ─────────────────────────────────────────────────────
|
|
23
23
|
/**
|
|
24
24
|
* Create an Ollama-based embedder function.
|
|
25
|
+
*
|
|
26
|
+
* Uses the /api/embed endpoint which returns prompt_eval_count (real token count).
|
|
25
27
|
*/
|
|
26
28
|
function createOllamaEmbedder(baseUrl = 'http://localhost:11434', model = 'nomic-embed-text') {
|
|
27
29
|
const client = new ollama_1.default(baseUrl);
|
|
@@ -55,27 +57,19 @@ function buildEmbedderFromModule(data) {
|
|
|
55
57
|
return buildEmbedder({
|
|
56
58
|
provider: data.embedding_provider,
|
|
57
59
|
model: data.embedding_model,
|
|
58
|
-
baseUrl: data.embedding_base_url
|
|
59
|
-
apiKey: data.api_key_ref,
|
|
60
|
+
baseUrl: data.embedding_base_url
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
|
-
* Resolve an embedder from environment variables
|
|
64
|
+
* Resolve an embedder from environment variables.
|
|
64
65
|
* This is a fallback for development when no llm_module or defaultEmbedder is configured.
|
|
65
66
|
*
|
|
66
|
-
* Environment variables (
|
|
67
|
-
* EMBEDDER_PROVIDER
|
|
68
|
-
* EMBEDDER_MODEL
|
|
69
|
-
* EMBEDDER_BASE_URL
|
|
67
|
+
* Environment variables (with defaults from env.ts):
|
|
68
|
+
* EMBEDDER_PROVIDER - Provider name (default: 'ollama')
|
|
69
|
+
* EMBEDDER_MODEL - Model identifier (default: 'nomic-embed-text')
|
|
70
|
+
* EMBEDDER_BASE_URL - Provider base URL (default: 'http://localhost:11434')
|
|
70
71
|
*/
|
|
71
72
|
function buildEmbedderFromEnv() {
|
|
72
|
-
const {
|
|
73
|
-
|
|
74
|
-
if (!provider)
|
|
75
|
-
return null;
|
|
76
|
-
return buildEmbedder({
|
|
77
|
-
provider,
|
|
78
|
-
model: llm?.embedder?.model,
|
|
79
|
-
baseUrl: llm?.embedder?.baseUrl,
|
|
80
|
-
});
|
|
73
|
+
const { embedding } = (0, env_1.getLlmEnvOptions)();
|
|
74
|
+
return buildEmbedder(embedding);
|
|
81
75
|
}
|
package/env.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Environment Configuration
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all LLM-related environment variables and defaults.
|
|
5
|
+
* Every other module in graphile-llm imports from here — no direct process.env
|
|
6
|
+
* reads or scattered null coalescing elsewhere.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* EMBEDDER_PROVIDER - Embedding provider name ('ollama')
|
|
10
|
+
* EMBEDDER_MODEL - Embedding model (default: 'nomic-embed-text')
|
|
11
|
+
* EMBEDDER_BASE_URL - Embedding provider URL (default: 'http://localhost:11434')
|
|
12
|
+
* CHAT_PROVIDER - Chat provider name ('ollama')
|
|
13
|
+
* CHAT_MODEL - Chat model (default: 'llama3')
|
|
14
|
+
* CHAT_BASE_URL - Chat provider URL (default: 'http://localhost:11434')
|
|
15
|
+
*/
|
|
16
|
+
export interface LlmProviderConfig {
|
|
17
|
+
provider: string;
|
|
18
|
+
model: string;
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
}
|
|
21
|
+
export interface LlmEnvOptions {
|
|
22
|
+
embedding: LlmProviderConfig;
|
|
23
|
+
chat: LlmProviderConfig;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve LLM configuration from environment variables with sensible defaults.
|
|
27
|
+
*
|
|
28
|
+
* Call this once and pass the result around — never read process.env directly
|
|
29
|
+
* in plugin code.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getLlmEnvOptions(): LlmEnvOptions;
|
package/env.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Environment Configuration
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for all LLM-related environment variables and defaults.
|
|
6
|
+
* Every other module in graphile-llm imports from here — no direct process.env
|
|
7
|
+
* reads or scattered null coalescing elsewhere.
|
|
8
|
+
*
|
|
9
|
+
* Environment variables:
|
|
10
|
+
* EMBEDDER_PROVIDER - Embedding provider name ('ollama')
|
|
11
|
+
* EMBEDDER_MODEL - Embedding model (default: 'nomic-embed-text')
|
|
12
|
+
* EMBEDDER_BASE_URL - Embedding provider URL (default: 'http://localhost:11434')
|
|
13
|
+
* CHAT_PROVIDER - Chat provider name ('ollama')
|
|
14
|
+
* CHAT_MODEL - Chat model (default: 'llama3')
|
|
15
|
+
* CHAT_BASE_URL - Chat provider URL (default: 'http://localhost:11434')
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.getLlmEnvOptions = getLlmEnvOptions;
|
|
19
|
+
// ─── Defaults ───────────────────────────────────────────────────────────────
|
|
20
|
+
const LLM_DEFAULTS = {
|
|
21
|
+
embedding: {
|
|
22
|
+
provider: 'ollama',
|
|
23
|
+
model: 'nomic-embed-text',
|
|
24
|
+
baseUrl: 'http://localhost:11434'
|
|
25
|
+
},
|
|
26
|
+
chat: {
|
|
27
|
+
provider: 'ollama',
|
|
28
|
+
model: 'llama3',
|
|
29
|
+
baseUrl: 'http://localhost:11434'
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
// ─── Resolution ─────────────────────────────────────────────────────────────
|
|
33
|
+
/**
|
|
34
|
+
* Resolve LLM configuration from environment variables with sensible defaults.
|
|
35
|
+
*
|
|
36
|
+
* Call this once and pass the result around — never read process.env directly
|
|
37
|
+
* in plugin code.
|
|
38
|
+
*/
|
|
39
|
+
function getLlmEnvOptions() {
|
|
40
|
+
return {
|
|
41
|
+
embedding: {
|
|
42
|
+
provider: process.env.EMBEDDER_PROVIDER ?? LLM_DEFAULTS.embedding.provider,
|
|
43
|
+
model: process.env.EMBEDDER_MODEL ?? LLM_DEFAULTS.embedding.model,
|
|
44
|
+
baseUrl: process.env.EMBEDDER_BASE_URL ?? LLM_DEFAULTS.embedding.baseUrl
|
|
45
|
+
},
|
|
46
|
+
chat: {
|
|
47
|
+
provider: process.env.CHAT_PROVIDER ?? LLM_DEFAULTS.chat.provider,
|
|
48
|
+
model: process.env.CHAT_MODEL ?? LLM_DEFAULTS.chat.model,
|
|
49
|
+
baseUrl: process.env.CHAT_BASE_URL ?? LLM_DEFAULTS.chat.baseUrl
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|