@constructive-io/express-context 0.4.0 → 0.6.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/README.md +15 -0
- package/billing-client.d.ts +56 -0
- package/billing-client.js +84 -0
- package/context.d.ts +1 -1
- package/context.js +38 -5
- package/esm/billing-client.js +81 -0
- package/esm/context.js +38 -5
- package/esm/index.js +2 -1
- package/esm/loaders/database-settings.js +3 -1
- package/esm/loaders/index.js +3 -0
- package/esm/loaders/llm.js +51 -0
- package/index.d.ts +4 -2
- package/index.js +4 -1
- package/loaders/database-settings.js +3 -1
- package/loaders/index.d.ts +1 -0
- package/loaders/index.js +5 -1
- package/loaders/llm.d.ts +12 -0
- package/loaders/llm.js +54 -0
- package/package.json +2 -2
- package/types.d.ts +37 -0
package/README.md
CHANGED
|
@@ -136,6 +136,21 @@ Common issues and solutions for pgpm, PostgreSQL, and testing.
|
|
|
136
136
|
* [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
|
|
137
137
|
* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.
|
|
138
138
|
|
|
139
|
+
### 📚 Documentation & Skills
|
|
140
|
+
|
|
141
|
+
* [constructive-skills](https://github.com/constructive-io/constructive-skills): **📖 Platform documentation and AI agent skills** — feature catalog, blueprint reference, SDK guides (i18n, billing, limits, events, uploads, security, entities, search, AI), and deployment guides.
|
|
142
|
+
|
|
143
|
+
Install skills for AI coding agents:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# All platform skills (security, blueprints, codegen, billing, etc.)
|
|
147
|
+
npx skills add constructive-io/constructive-skills
|
|
148
|
+
|
|
149
|
+
# Individual repo skills (pgpm, testing, CLI, search, etc.)
|
|
150
|
+
npx skills add https://github.com/constructive-io/constructive --skill pgpm
|
|
151
|
+
npx skills add https://github.com/constructive-io/constructive --skill constructive-testing
|
|
152
|
+
```
|
|
153
|
+
|
|
139
154
|
## Credits
|
|
140
155
|
|
|
141
156
|
**🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* billing-client — Shared billing operations for any Express-based service
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified `BillingClient` that encapsulates:
|
|
5
|
+
* - `checkQuota(meterSlug, amount)` — pre-check billing quota
|
|
6
|
+
* - `recordUsage(meterSlug, amount, metadata)` — post-record usage
|
|
7
|
+
* - `logInference(entry)` — write to usage_log_inference table
|
|
8
|
+
*
|
|
9
|
+
* Both `agentic-server` and `graphile-llm` should delegate to this client
|
|
10
|
+
* instead of implementing their own SQL calls.
|
|
11
|
+
*
|
|
12
|
+
* Graceful: all operations are no-ops if the billing/inference_log modules
|
|
13
|
+
* are not provisioned. Errors are logged and swallowed (non-fatal).
|
|
14
|
+
*/
|
|
15
|
+
import type { BillingConfig, InferenceLogConfig, WithPgClient } from './types';
|
|
16
|
+
export interface InferenceLogEntry {
|
|
17
|
+
entityId: string;
|
|
18
|
+
actorId: string | null;
|
|
19
|
+
model: string;
|
|
20
|
+
provider: string | null;
|
|
21
|
+
service: string;
|
|
22
|
+
operation: string;
|
|
23
|
+
inputTokens: number;
|
|
24
|
+
outputTokens: number;
|
|
25
|
+
totalTokens: number;
|
|
26
|
+
latencyMs: number;
|
|
27
|
+
status: string;
|
|
28
|
+
cacheReadTokens?: number | null;
|
|
29
|
+
cacheWriteTokens?: number | null;
|
|
30
|
+
ragEnabled?: boolean;
|
|
31
|
+
chunksRetrieved?: number | null;
|
|
32
|
+
embeddingModel?: string | null;
|
|
33
|
+
embeddingLatencyMs?: number | null;
|
|
34
|
+
errorType?: string | null;
|
|
35
|
+
rawUsage?: Record<string, unknown> | null;
|
|
36
|
+
}
|
|
37
|
+
export interface BillingClient {
|
|
38
|
+
/**
|
|
39
|
+
* Check if the entity has sufficient quota for the requested amount.
|
|
40
|
+
* Returns true if allowed, false if quota is exceeded.
|
|
41
|
+
*
|
|
42
|
+
* Gracefully returns true if billing is not provisioned or errors.
|
|
43
|
+
*/
|
|
44
|
+
checkQuota(meterSlug: string, amount?: number): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Record usage after a successful operation.
|
|
47
|
+
* Gracefully skips if billing is not provisioned or errors.
|
|
48
|
+
*/
|
|
49
|
+
recordUsage(meterSlug: string, amount: number, metadata?: Record<string, unknown>): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Write a row to the usage_log_inference table.
|
|
52
|
+
* Gracefully skips if inference_log_module is not provisioned or errors.
|
|
53
|
+
*/
|
|
54
|
+
logInference(entry: InferenceLogEntry): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export declare function createBillingClient(withPgClient: WithPgClient, entityId: string, billing: BillingConfig | null, inferenceLog: InferenceLogConfig | null): BillingClient;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* billing-client — Shared billing operations for any Express-based service
|
|
4
|
+
*
|
|
5
|
+
* Provides a unified `BillingClient` that encapsulates:
|
|
6
|
+
* - `checkQuota(meterSlug, amount)` — pre-check billing quota
|
|
7
|
+
* - `recordUsage(meterSlug, amount, metadata)` — post-record usage
|
|
8
|
+
* - `logInference(entry)` — write to usage_log_inference table
|
|
9
|
+
*
|
|
10
|
+
* Both `agentic-server` and `graphile-llm` should delegate to this client
|
|
11
|
+
* instead of implementing their own SQL calls.
|
|
12
|
+
*
|
|
13
|
+
* Graceful: all operations are no-ops if the billing/inference_log modules
|
|
14
|
+
* are not provisioned. Errors are logged and swallowed (non-fatal).
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createBillingClient = createBillingClient;
|
|
18
|
+
const logger_1 = require("@pgpmjs/logger");
|
|
19
|
+
const log = new logger_1.Logger('billing-client');
|
|
20
|
+
// ─── Implementation ─────────────────────────────────────────────────────────
|
|
21
|
+
function createBillingClient(withPgClient, entityId, billing, inferenceLog) {
|
|
22
|
+
return {
|
|
23
|
+
async checkQuota(meterSlug, amount = 1) {
|
|
24
|
+
if (!billing)
|
|
25
|
+
return true;
|
|
26
|
+
try {
|
|
27
|
+
return await withPgClient(async (client) => {
|
|
28
|
+
const sql = `SELECT "${billing.privateSchema}"."${billing.checkBillingQuotaFunction}"($1, $2::uuid, $3) AS allowed`;
|
|
29
|
+
const result = await client.query(sql, [meterSlug, entityId, amount]);
|
|
30
|
+
return result.rows[0]?.allowed !== false;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
35
|
+
log.warn(`check_billing_quota failed (allowing): ${message}`);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
async recordUsage(meterSlug, amount, metadata) {
|
|
40
|
+
if (!billing)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
await withPgClient(async (client) => {
|
|
44
|
+
const sql = `SELECT "${billing.privateSchema}"."${billing.recordUsageFunction}"($1, $2::uuid, $3, $4::jsonb)`;
|
|
45
|
+
await client.query(sql, [meterSlug, entityId, amount, JSON.stringify(metadata ?? {})]);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
50
|
+
log.warn(`record_usage failed (non-fatal): ${message}`);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
async logInference(entry) {
|
|
54
|
+
if (!inferenceLog)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
await withPgClient(async (client) => {
|
|
58
|
+
await client.query(`INSERT INTO "${inferenceLog.schema}"."${inferenceLog.tableName}"
|
|
59
|
+
(entity_id, actor_id, model, provider, service, operation,
|
|
60
|
+
input_tokens, output_tokens, total_tokens, latency_ms, status,
|
|
61
|
+
cache_read_tokens, cache_write_tokens,
|
|
62
|
+
rag_enabled, chunks_retrieved,
|
|
63
|
+
embedding_model, embedding_latency_ms,
|
|
64
|
+
error_type, raw_usage)
|
|
65
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`, [
|
|
66
|
+
entry.entityId, entry.actorId, entry.model,
|
|
67
|
+
entry.provider, entry.service, entry.operation,
|
|
68
|
+
entry.inputTokens, entry.outputTokens, entry.totalTokens,
|
|
69
|
+
entry.latencyMs, entry.status,
|
|
70
|
+
entry.cacheReadTokens ?? null, entry.cacheWriteTokens ?? null,
|
|
71
|
+
entry.ragEnabled ?? false, entry.chunksRetrieved ?? null,
|
|
72
|
+
entry.embeddingModel ?? null, entry.embeddingLatencyMs ?? null,
|
|
73
|
+
entry.errorType ?? null,
|
|
74
|
+
entry.rawUsage ? JSON.stringify(entry.rawUsage) : null
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
80
|
+
log.warn(`inference log INSERT failed (non-fatal): ${message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
package/context.d.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* The result is a single `req.constructive` object that any downstream
|
|
14
14
|
* route handler can use for tenant-scoped database operations.
|
|
15
15
|
*/
|
|
16
|
-
import type { Request, RequestHandler } from 'express';
|
|
17
16
|
import type { PgpmOptions } from '@pgpmjs/types';
|
|
17
|
+
import type { Request, RequestHandler } from 'express';
|
|
18
18
|
import type { LoaderRegistry } from './loaders/registry';
|
|
19
19
|
import type { ConstructiveContext } from './types';
|
|
20
20
|
export interface ContextMiddlewareOptions {
|
package/context.js
CHANGED
|
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.buildContext = buildContext;
|
|
19
19
|
exports.createContextMiddleware = createContextMiddleware;
|
|
20
20
|
const pg_cache_1 = require("pg-cache");
|
|
21
|
+
const billing_client_1 = require("./billing-client");
|
|
21
22
|
const pg_client_1 = require("./pg-client");
|
|
22
23
|
const pg_settings_1 = require("./pg-settings");
|
|
23
24
|
/**
|
|
@@ -54,11 +55,11 @@ function buildContext(req, opts = {}) {
|
|
|
54
55
|
api,
|
|
55
56
|
token,
|
|
56
57
|
requestId,
|
|
57
|
-
clientIp: req.clientIp
|
|
58
|
+
clientIp: req.clientIp
|
|
58
59
|
});
|
|
59
60
|
const tenantPool = (0, pg_cache_1.getPgPool)({
|
|
60
61
|
...opts.pg,
|
|
61
|
-
database: api.dbname
|
|
62
|
+
database: api.dbname
|
|
62
63
|
});
|
|
63
64
|
// Build loader context (if registry provided and databaseId known)
|
|
64
65
|
let loaderCtx = null;
|
|
@@ -69,9 +70,15 @@ function buildContext(req, opts = {}) {
|
|
|
69
70
|
tenantPool,
|
|
70
71
|
databaseId: api.databaseId,
|
|
71
72
|
apiId: api.apiId,
|
|
72
|
-
dbname: api.dbname
|
|
73
|
+
dbname: api.dbname
|
|
73
74
|
};
|
|
74
75
|
}
|
|
76
|
+
const withPgClient = (fn) => (0, pg_client_1.withPgClient)(tenantPool, pgSettings, fn);
|
|
77
|
+
const useModule = createUseModule(opts.loaders, loaderCtx);
|
|
78
|
+
// Lazy-initialized billing client (cached per request)
|
|
79
|
+
let billingClient;
|
|
80
|
+
// Lazy-initialized LLM config (cached per request)
|
|
81
|
+
let llmConfig;
|
|
75
82
|
return {
|
|
76
83
|
api,
|
|
77
84
|
token,
|
|
@@ -80,8 +87,34 @@ function buildContext(req, opts = {}) {
|
|
|
80
87
|
userId: token?.user_id ?? null,
|
|
81
88
|
requestId,
|
|
82
89
|
pool: tenantPool,
|
|
83
|
-
withPgClient
|
|
84
|
-
useModule
|
|
90
|
+
withPgClient,
|
|
91
|
+
useModule,
|
|
92
|
+
async useBilling() {
|
|
93
|
+
if (billingClient !== undefined)
|
|
94
|
+
return billingClient;
|
|
95
|
+
const entityId = token?.entity_id;
|
|
96
|
+
if (!entityId) {
|
|
97
|
+
billingClient = null;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const [billing, inferenceLog] = await Promise.all([
|
|
101
|
+
useModule('billing'),
|
|
102
|
+
useModule('inferenceLog')
|
|
103
|
+
]);
|
|
104
|
+
if (!billing) {
|
|
105
|
+
billingClient = null;
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
billingClient = (0, billing_client_1.createBillingClient)(withPgClient, entityId, billing, inferenceLog ?? null);
|
|
109
|
+
return billingClient;
|
|
110
|
+
},
|
|
111
|
+
async useLlm() {
|
|
112
|
+
if (llmConfig !== undefined)
|
|
113
|
+
return llmConfig;
|
|
114
|
+
const resolved = await useModule('llm');
|
|
115
|
+
llmConfig = resolved ?? null;
|
|
116
|
+
return llmConfig;
|
|
117
|
+
}
|
|
85
118
|
};
|
|
86
119
|
}
|
|
87
120
|
/**
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* billing-client — Shared billing operations for any Express-based service
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified `BillingClient` that encapsulates:
|
|
5
|
+
* - `checkQuota(meterSlug, amount)` — pre-check billing quota
|
|
6
|
+
* - `recordUsage(meterSlug, amount, metadata)` — post-record usage
|
|
7
|
+
* - `logInference(entry)` — write to usage_log_inference table
|
|
8
|
+
*
|
|
9
|
+
* Both `agentic-server` and `graphile-llm` should delegate to this client
|
|
10
|
+
* instead of implementing their own SQL calls.
|
|
11
|
+
*
|
|
12
|
+
* Graceful: all operations are no-ops if the billing/inference_log modules
|
|
13
|
+
* are not provisioned. Errors are logged and swallowed (non-fatal).
|
|
14
|
+
*/
|
|
15
|
+
import { Logger } from '@pgpmjs/logger';
|
|
16
|
+
const log = new Logger('billing-client');
|
|
17
|
+
// ─── Implementation ─────────────────────────────────────────────────────────
|
|
18
|
+
export function createBillingClient(withPgClient, entityId, billing, inferenceLog) {
|
|
19
|
+
return {
|
|
20
|
+
async checkQuota(meterSlug, amount = 1) {
|
|
21
|
+
if (!billing)
|
|
22
|
+
return true;
|
|
23
|
+
try {
|
|
24
|
+
return await withPgClient(async (client) => {
|
|
25
|
+
const sql = `SELECT "${billing.privateSchema}"."${billing.checkBillingQuotaFunction}"($1, $2::uuid, $3) AS allowed`;
|
|
26
|
+
const result = await client.query(sql, [meterSlug, entityId, amount]);
|
|
27
|
+
return result.rows[0]?.allowed !== false;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
32
|
+
log.warn(`check_billing_quota failed (allowing): ${message}`);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
async recordUsage(meterSlug, amount, metadata) {
|
|
37
|
+
if (!billing)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
await withPgClient(async (client) => {
|
|
41
|
+
const sql = `SELECT "${billing.privateSchema}"."${billing.recordUsageFunction}"($1, $2::uuid, $3, $4::jsonb)`;
|
|
42
|
+
await client.query(sql, [meterSlug, entityId, amount, JSON.stringify(metadata ?? {})]);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
47
|
+
log.warn(`record_usage failed (non-fatal): ${message}`);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async logInference(entry) {
|
|
51
|
+
if (!inferenceLog)
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
await withPgClient(async (client) => {
|
|
55
|
+
await client.query(`INSERT INTO "${inferenceLog.schema}"."${inferenceLog.tableName}"
|
|
56
|
+
(entity_id, actor_id, model, provider, service, operation,
|
|
57
|
+
input_tokens, output_tokens, total_tokens, latency_ms, status,
|
|
58
|
+
cache_read_tokens, cache_write_tokens,
|
|
59
|
+
rag_enabled, chunks_retrieved,
|
|
60
|
+
embedding_model, embedding_latency_ms,
|
|
61
|
+
error_type, raw_usage)
|
|
62
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`, [
|
|
63
|
+
entry.entityId, entry.actorId, entry.model,
|
|
64
|
+
entry.provider, entry.service, entry.operation,
|
|
65
|
+
entry.inputTokens, entry.outputTokens, entry.totalTokens,
|
|
66
|
+
entry.latencyMs, entry.status,
|
|
67
|
+
entry.cacheReadTokens ?? null, entry.cacheWriteTokens ?? null,
|
|
68
|
+
entry.ragEnabled ?? false, entry.chunksRetrieved ?? null,
|
|
69
|
+
entry.embeddingModel ?? null, entry.embeddingLatencyMs ?? null,
|
|
70
|
+
entry.errorType ?? null,
|
|
71
|
+
entry.rawUsage ? JSON.stringify(entry.rawUsage) : null
|
|
72
|
+
]);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
77
|
+
log.warn(`inference log INSERT failed (non-fatal): ${message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
package/esm/context.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* route handler can use for tenant-scoped database operations.
|
|
15
15
|
*/
|
|
16
16
|
import { getPgPool } from 'pg-cache';
|
|
17
|
+
import { createBillingClient } from './billing-client';
|
|
17
18
|
import { withPgClient as withPgClientFn } from './pg-client';
|
|
18
19
|
import { buildPgSettings } from './pg-settings';
|
|
19
20
|
/**
|
|
@@ -50,11 +51,11 @@ export function buildContext(req, opts = {}) {
|
|
|
50
51
|
api,
|
|
51
52
|
token,
|
|
52
53
|
requestId,
|
|
53
|
-
clientIp: req.clientIp
|
|
54
|
+
clientIp: req.clientIp
|
|
54
55
|
});
|
|
55
56
|
const tenantPool = getPgPool({
|
|
56
57
|
...opts.pg,
|
|
57
|
-
database: api.dbname
|
|
58
|
+
database: api.dbname
|
|
58
59
|
});
|
|
59
60
|
// Build loader context (if registry provided and databaseId known)
|
|
60
61
|
let loaderCtx = null;
|
|
@@ -65,9 +66,15 @@ export function buildContext(req, opts = {}) {
|
|
|
65
66
|
tenantPool,
|
|
66
67
|
databaseId: api.databaseId,
|
|
67
68
|
apiId: api.apiId,
|
|
68
|
-
dbname: api.dbname
|
|
69
|
+
dbname: api.dbname
|
|
69
70
|
};
|
|
70
71
|
}
|
|
72
|
+
const withPgClient = (fn) => withPgClientFn(tenantPool, pgSettings, fn);
|
|
73
|
+
const useModule = createUseModule(opts.loaders, loaderCtx);
|
|
74
|
+
// Lazy-initialized billing client (cached per request)
|
|
75
|
+
let billingClient;
|
|
76
|
+
// Lazy-initialized LLM config (cached per request)
|
|
77
|
+
let llmConfig;
|
|
71
78
|
return {
|
|
72
79
|
api,
|
|
73
80
|
token,
|
|
@@ -76,8 +83,34 @@ export function buildContext(req, opts = {}) {
|
|
|
76
83
|
userId: token?.user_id ?? null,
|
|
77
84
|
requestId,
|
|
78
85
|
pool: tenantPool,
|
|
79
|
-
withPgClient
|
|
80
|
-
useModule
|
|
86
|
+
withPgClient,
|
|
87
|
+
useModule,
|
|
88
|
+
async useBilling() {
|
|
89
|
+
if (billingClient !== undefined)
|
|
90
|
+
return billingClient;
|
|
91
|
+
const entityId = token?.entity_id;
|
|
92
|
+
if (!entityId) {
|
|
93
|
+
billingClient = null;
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const [billing, inferenceLog] = await Promise.all([
|
|
97
|
+
useModule('billing'),
|
|
98
|
+
useModule('inferenceLog')
|
|
99
|
+
]);
|
|
100
|
+
if (!billing) {
|
|
101
|
+
billingClient = null;
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
billingClient = createBillingClient(withPgClient, entityId, billing, inferenceLog ?? null);
|
|
105
|
+
return billingClient;
|
|
106
|
+
},
|
|
107
|
+
async useLlm() {
|
|
108
|
+
if (llmConfig !== undefined)
|
|
109
|
+
return llmConfig;
|
|
110
|
+
const resolved = await useModule('llm');
|
|
111
|
+
llmConfig = resolved ?? null;
|
|
112
|
+
return llmConfig;
|
|
113
|
+
}
|
|
81
114
|
};
|
|
82
115
|
}
|
|
83
116
|
/**
|
package/esm/index.js
CHANGED
|
@@ -34,12 +34,13 @@
|
|
|
34
34
|
* });
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
+
export { createBillingClient } from './billing-client';
|
|
37
38
|
export { buildPgSettings } from './pg-settings';
|
|
38
39
|
// withPgClient helper
|
|
39
40
|
export { withPgClient } from './pg-client';
|
|
40
41
|
// Request ID middleware
|
|
41
42
|
export { requestIdMiddleware } from './request-id';
|
|
42
43
|
export { buildContext, createContextMiddleware } from './context';
|
|
43
|
-
export { agentChatLoader, authSettingsLoader, billingLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, inferenceLogLoader, pubkeyLoader, rlsLoader, webauthnLoader, } from './loaders';
|
|
44
|
+
export { agentChatLoader, authSettingsLoader, billingLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, inferenceLogLoader, pubkeyLoader, rlsLoader, llmLoader, webauthnLoader, } from './loaders';
|
|
44
45
|
// Side-effect: Express type augmentation
|
|
45
46
|
import './types';
|
|
@@ -19,7 +19,8 @@ const DATABASE_SETTINGS_SQL = `
|
|
|
19
19
|
COALESCE(aps.enable_ltree, ds.enable_ltree) AS resolved_enable_ltree,
|
|
20
20
|
COALESCE(aps.enable_llm, ds.enable_llm) AS resolved_enable_llm,
|
|
21
21
|
COALESCE(aps.enable_realtime, ds.enable_realtime) AS resolved_enable_realtime,
|
|
22
|
-
COALESCE(aps.enable_bulk, ds.enable_bulk) AS resolved_enable_bulk
|
|
22
|
+
COALESCE(aps.enable_bulk, ds.enable_bulk) AS resolved_enable_bulk,
|
|
23
|
+
COALESCE(aps.enable_i18n, ds.enable_i18n) AS resolved_enable_i18n
|
|
23
24
|
FROM services_public.database_settings ds
|
|
24
25
|
LEFT JOIN services_public.api_settings aps ON ds.database_id = aps.database_id AND aps.api_id = $2
|
|
25
26
|
WHERE ds.database_id = $1
|
|
@@ -47,6 +48,7 @@ export const databaseSettingsLoader = createModuleLoader({
|
|
|
47
48
|
enableLlm: row.resolved_enable_llm,
|
|
48
49
|
enableRealtime: row.resolved_enable_realtime,
|
|
49
50
|
enableBulk: row.resolved_enable_bulk,
|
|
51
|
+
enableI18n: row.resolved_enable_i18n,
|
|
50
52
|
};
|
|
51
53
|
},
|
|
52
54
|
});
|
package/esm/loaders/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export { authSettingsLoader } from './auth-settings';
|
|
|
37
37
|
export { billingLoader } from './billing';
|
|
38
38
|
export { inferenceLogLoader } from './inference-log';
|
|
39
39
|
export { agentChatLoader } from './agent-chat';
|
|
40
|
+
export { llmLoader } from './llm';
|
|
40
41
|
/**
|
|
41
42
|
* Convenience: create a registry pre-loaded with all built-in loaders.
|
|
42
43
|
*/
|
|
@@ -50,6 +51,7 @@ import { authSettingsLoader } from './auth-settings';
|
|
|
50
51
|
import { billingLoader } from './billing';
|
|
51
52
|
import { inferenceLogLoader } from './inference-log';
|
|
52
53
|
import { agentChatLoader } from './agent-chat';
|
|
54
|
+
import { llmLoader } from './llm';
|
|
53
55
|
export function createDefaultRegistry() {
|
|
54
56
|
const registry = createLoaderRegistry();
|
|
55
57
|
registry.register(rlsLoader);
|
|
@@ -61,5 +63,6 @@ export function createDefaultRegistry() {
|
|
|
61
63
|
registry.register(billingLoader);
|
|
62
64
|
registry.register(inferenceLogLoader);
|
|
63
65
|
registry.register(agentChatLoader);
|
|
66
|
+
registry.register(llmLoader);
|
|
64
67
|
return registry;
|
|
65
68
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Module Loader
|
|
3
|
+
*
|
|
4
|
+
* Resolves per-database LLM provider config from metaschema_modules_public.llm_module.
|
|
5
|
+
* Returns embedding and chat provider settings (model, base URL, dimensions, limits).
|
|
6
|
+
*
|
|
7
|
+
* When the llm_module table doesn't exist yet (pre-migration), the loader
|
|
8
|
+
* gracefully returns undefined — callers fall back to env vars.
|
|
9
|
+
*/
|
|
10
|
+
import { createModuleLoader } from './create-loader';
|
|
11
|
+
// ─── SQL ────────────────────────────────────────────────────────────────────
|
|
12
|
+
const LLM_MODULE_SQL = `
|
|
13
|
+
SELECT
|
|
14
|
+
lm.embedding_provider,
|
|
15
|
+
lm.embedding_model,
|
|
16
|
+
lm.embedding_base_url,
|
|
17
|
+
lm.embedding_dimensions,
|
|
18
|
+
lm.chat_provider,
|
|
19
|
+
lm.chat_model,
|
|
20
|
+
lm.chat_base_url,
|
|
21
|
+
lm.rate_limit_rpm,
|
|
22
|
+
lm.max_tokens_per_request,
|
|
23
|
+
lm.rag_context_limit
|
|
24
|
+
FROM metaschema_modules_public.llm_module lm
|
|
25
|
+
WHERE lm.database_id = $1
|
|
26
|
+
LIMIT 1
|
|
27
|
+
`;
|
|
28
|
+
// ─── Loader ─────────────────────────────────────────────────────────────────
|
|
29
|
+
export const llmLoader = createModuleLoader({
|
|
30
|
+
name: 'llm',
|
|
31
|
+
ttlMs: 5 * 60_000,
|
|
32
|
+
async resolve(ctx) {
|
|
33
|
+
const { tenantPool, databaseId } = ctx;
|
|
34
|
+
const result = await tenantPool.query(LLM_MODULE_SQL, [databaseId]);
|
|
35
|
+
const row = result.rows[0];
|
|
36
|
+
if (!row)
|
|
37
|
+
return undefined;
|
|
38
|
+
return {
|
|
39
|
+
embeddingProvider: row.embedding_provider,
|
|
40
|
+
embeddingModel: row.embedding_model,
|
|
41
|
+
embeddingBaseUrl: row.embedding_base_url,
|
|
42
|
+
embeddingDimensions: row.embedding_dimensions,
|
|
43
|
+
chatProvider: row.chat_provider,
|
|
44
|
+
chatModel: row.chat_model,
|
|
45
|
+
chatBaseUrl: row.chat_base_url,
|
|
46
|
+
rateLimitRpm: row.rate_limit_rpm,
|
|
47
|
+
maxTokensPerRequest: row.max_tokens_per_request,
|
|
48
|
+
ragContextLimit: row.rag_context_limit,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
package/index.d.ts
CHANGED
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
* });
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
|
-
export type { AgentChatConfig, ApiConfigResult, ApiError, ApiModule, ApiStructure, AuthSettings, BillingConfig, BuiltinModuleMap, ConstructiveAPIToken, ConstructiveContext, CorsModuleData, DatabaseSettings, GenericModuleData, InferenceLogConfig, PublicKeyChallengeData, PubkeyChallengeSettings, RlsModule, WebauthnSettings, WithPgClient, } from './types';
|
|
37
|
+
export type { AgentChatConfig, ApiConfigResult, ApiError, ApiModule, ApiStructure, AuthSettings, BillingConfig, BuiltinModuleMap, ConstructiveAPIToken, ConstructiveContext, CorsModuleData, DatabaseSettings, GenericModuleData, InferenceLogConfig, LlmConfig, PublicKeyChallengeData, PubkeyChallengeSettings, RlsModule, WebauthnSettings, WithPgClient, } from './types';
|
|
38
|
+
export type { BillingClient, InferenceLogEntry } from './billing-client';
|
|
39
|
+
export { createBillingClient } from './billing-client';
|
|
38
40
|
export type { PgSettingsInput } from './pg-settings';
|
|
39
41
|
export { buildPgSettings } from './pg-settings';
|
|
40
42
|
export { withPgClient } from './pg-client';
|
|
@@ -42,5 +44,5 @@ export { requestIdMiddleware } from './request-id';
|
|
|
42
44
|
export type { ContextMiddlewareOptions } from './context';
|
|
43
45
|
export { buildContext, createContextMiddleware } from './context';
|
|
44
46
|
export type { CreateLoaderOptions, LoaderContext, LoaderRegistry, ModuleLoader, } from './loaders';
|
|
45
|
-
export { agentChatLoader, authSettingsLoader, billingLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, inferenceLogLoader, pubkeyLoader, rlsLoader, webauthnLoader, } from './loaders';
|
|
47
|
+
export { agentChatLoader, authSettingsLoader, billingLoader, corsLoader, createDefaultRegistry, createLoaderRegistry, createModuleLoader, databaseSettingsLoader, inferenceLogLoader, pubkeyLoader, rlsLoader, llmLoader, webauthnLoader, } from './loaders';
|
|
46
48
|
import './types';
|
package/index.js
CHANGED
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.webauthnLoader = exports.rlsLoader = exports.pubkeyLoader = exports.inferenceLogLoader = exports.databaseSettingsLoader = exports.createModuleLoader = exports.createLoaderRegistry = exports.createDefaultRegistry = exports.corsLoader = exports.billingLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createContextMiddleware = exports.buildContext = exports.requestIdMiddleware = exports.withPgClient = exports.buildPgSettings = void 0;
|
|
39
|
+
exports.webauthnLoader = exports.llmLoader = exports.rlsLoader = exports.pubkeyLoader = exports.inferenceLogLoader = exports.databaseSettingsLoader = exports.createModuleLoader = exports.createLoaderRegistry = exports.createDefaultRegistry = exports.corsLoader = exports.billingLoader = exports.authSettingsLoader = exports.agentChatLoader = exports.createContextMiddleware = exports.buildContext = exports.requestIdMiddleware = exports.withPgClient = exports.buildPgSettings = exports.createBillingClient = void 0;
|
|
40
|
+
var billing_client_1 = require("./billing-client");
|
|
41
|
+
Object.defineProperty(exports, "createBillingClient", { enumerable: true, get: function () { return billing_client_1.createBillingClient; } });
|
|
40
42
|
var pg_settings_1 = require("./pg-settings");
|
|
41
43
|
Object.defineProperty(exports, "buildPgSettings", { enumerable: true, get: function () { return pg_settings_1.buildPgSettings; } });
|
|
42
44
|
// withPgClient helper
|
|
@@ -60,6 +62,7 @@ Object.defineProperty(exports, "databaseSettingsLoader", { enumerable: true, get
|
|
|
60
62
|
Object.defineProperty(exports, "inferenceLogLoader", { enumerable: true, get: function () { return loaders_1.inferenceLogLoader; } });
|
|
61
63
|
Object.defineProperty(exports, "pubkeyLoader", { enumerable: true, get: function () { return loaders_1.pubkeyLoader; } });
|
|
62
64
|
Object.defineProperty(exports, "rlsLoader", { enumerable: true, get: function () { return loaders_1.rlsLoader; } });
|
|
65
|
+
Object.defineProperty(exports, "llmLoader", { enumerable: true, get: function () { return loaders_1.llmLoader; } });
|
|
63
66
|
Object.defineProperty(exports, "webauthnLoader", { enumerable: true, get: function () { return loaders_1.webauthnLoader; } });
|
|
64
67
|
// Side-effect: Express type augmentation
|
|
65
68
|
require("./types");
|
|
@@ -22,7 +22,8 @@ const DATABASE_SETTINGS_SQL = `
|
|
|
22
22
|
COALESCE(aps.enable_ltree, ds.enable_ltree) AS resolved_enable_ltree,
|
|
23
23
|
COALESCE(aps.enable_llm, ds.enable_llm) AS resolved_enable_llm,
|
|
24
24
|
COALESCE(aps.enable_realtime, ds.enable_realtime) AS resolved_enable_realtime,
|
|
25
|
-
COALESCE(aps.enable_bulk, ds.enable_bulk) AS resolved_enable_bulk
|
|
25
|
+
COALESCE(aps.enable_bulk, ds.enable_bulk) AS resolved_enable_bulk,
|
|
26
|
+
COALESCE(aps.enable_i18n, ds.enable_i18n) AS resolved_enable_i18n
|
|
26
27
|
FROM services_public.database_settings ds
|
|
27
28
|
LEFT JOIN services_public.api_settings aps ON ds.database_id = aps.database_id AND aps.api_id = $2
|
|
28
29
|
WHERE ds.database_id = $1
|
|
@@ -50,6 +51,7 @@ exports.databaseSettingsLoader = (0, create_loader_1.createModuleLoader)({
|
|
|
50
51
|
enableLlm: row.resolved_enable_llm,
|
|
51
52
|
enableRealtime: row.resolved_enable_realtime,
|
|
52
53
|
enableBulk: row.resolved_enable_bulk,
|
|
54
|
+
enableI18n: row.resolved_enable_i18n,
|
|
53
55
|
};
|
|
54
56
|
},
|
|
55
57
|
});
|
package/loaders/index.d.ts
CHANGED
|
@@ -39,4 +39,5 @@ export { authSettingsLoader } from './auth-settings';
|
|
|
39
39
|
export { billingLoader } from './billing';
|
|
40
40
|
export { inferenceLogLoader } from './inference-log';
|
|
41
41
|
export { agentChatLoader } from './agent-chat';
|
|
42
|
+
export { llmLoader } from './llm';
|
|
42
43
|
export declare function createDefaultRegistry(): import("./registry").LoaderRegistry;
|
package/loaders/index.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* registry.register(myLoader);
|
|
28
28
|
*/
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.agentChatLoader = exports.inferenceLogLoader = exports.billingLoader = exports.authSettingsLoader = exports.webauthnLoader = exports.pubkeyLoader = exports.databaseSettingsLoader = exports.corsLoader = exports.rlsLoader = exports.createLoaderRegistry = exports.createModuleLoader = void 0;
|
|
30
|
+
exports.llmLoader = exports.agentChatLoader = exports.inferenceLogLoader = exports.billingLoader = exports.authSettingsLoader = exports.webauthnLoader = exports.pubkeyLoader = exports.databaseSettingsLoader = exports.corsLoader = exports.rlsLoader = exports.createLoaderRegistry = exports.createModuleLoader = void 0;
|
|
31
31
|
exports.createDefaultRegistry = createDefaultRegistry;
|
|
32
32
|
var create_loader_1 = require("./create-loader");
|
|
33
33
|
Object.defineProperty(exports, "createModuleLoader", { enumerable: true, get: function () { return create_loader_1.createModuleLoader; } });
|
|
@@ -52,6 +52,8 @@ var inference_log_1 = require("./inference-log");
|
|
|
52
52
|
Object.defineProperty(exports, "inferenceLogLoader", { enumerable: true, get: function () { return inference_log_1.inferenceLogLoader; } });
|
|
53
53
|
var agent_chat_1 = require("./agent-chat");
|
|
54
54
|
Object.defineProperty(exports, "agentChatLoader", { enumerable: true, get: function () { return agent_chat_1.agentChatLoader; } });
|
|
55
|
+
var llm_1 = require("./llm");
|
|
56
|
+
Object.defineProperty(exports, "llmLoader", { enumerable: true, get: function () { return llm_1.llmLoader; } });
|
|
55
57
|
/**
|
|
56
58
|
* Convenience: create a registry pre-loaded with all built-in loaders.
|
|
57
59
|
*/
|
|
@@ -65,6 +67,7 @@ const auth_settings_2 = require("./auth-settings");
|
|
|
65
67
|
const billing_2 = require("./billing");
|
|
66
68
|
const inference_log_2 = require("./inference-log");
|
|
67
69
|
const agent_chat_2 = require("./agent-chat");
|
|
70
|
+
const llm_2 = require("./llm");
|
|
68
71
|
function createDefaultRegistry() {
|
|
69
72
|
const registry = (0, registry_2.createLoaderRegistry)();
|
|
70
73
|
registry.register(rls_2.rlsLoader);
|
|
@@ -76,5 +79,6 @@ function createDefaultRegistry() {
|
|
|
76
79
|
registry.register(billing_2.billingLoader);
|
|
77
80
|
registry.register(inference_log_2.inferenceLogLoader);
|
|
78
81
|
registry.register(agent_chat_2.agentChatLoader);
|
|
82
|
+
registry.register(llm_2.llmLoader);
|
|
79
83
|
return registry;
|
|
80
84
|
}
|
package/loaders/llm.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Module Loader
|
|
3
|
+
*
|
|
4
|
+
* Resolves per-database LLM provider config from metaschema_modules_public.llm_module.
|
|
5
|
+
* Returns embedding and chat provider settings (model, base URL, dimensions, limits).
|
|
6
|
+
*
|
|
7
|
+
* When the llm_module table doesn't exist yet (pre-migration), the loader
|
|
8
|
+
* gracefully returns undefined — callers fall back to env vars.
|
|
9
|
+
*/
|
|
10
|
+
import type { LlmConfig } from '../types';
|
|
11
|
+
import type { ModuleLoader } from './types';
|
|
12
|
+
export declare const llmLoader: ModuleLoader<LlmConfig>;
|
package/loaders/llm.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Module Loader
|
|
4
|
+
*
|
|
5
|
+
* Resolves per-database LLM provider config from metaschema_modules_public.llm_module.
|
|
6
|
+
* Returns embedding and chat provider settings (model, base URL, dimensions, limits).
|
|
7
|
+
*
|
|
8
|
+
* When the llm_module table doesn't exist yet (pre-migration), the loader
|
|
9
|
+
* gracefully returns undefined — callers fall back to env vars.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.llmLoader = void 0;
|
|
13
|
+
const create_loader_1 = require("./create-loader");
|
|
14
|
+
// ─── SQL ────────────────────────────────────────────────────────────────────
|
|
15
|
+
const LLM_MODULE_SQL = `
|
|
16
|
+
SELECT
|
|
17
|
+
lm.embedding_provider,
|
|
18
|
+
lm.embedding_model,
|
|
19
|
+
lm.embedding_base_url,
|
|
20
|
+
lm.embedding_dimensions,
|
|
21
|
+
lm.chat_provider,
|
|
22
|
+
lm.chat_model,
|
|
23
|
+
lm.chat_base_url,
|
|
24
|
+
lm.rate_limit_rpm,
|
|
25
|
+
lm.max_tokens_per_request,
|
|
26
|
+
lm.rag_context_limit
|
|
27
|
+
FROM metaschema_modules_public.llm_module lm
|
|
28
|
+
WHERE lm.database_id = $1
|
|
29
|
+
LIMIT 1
|
|
30
|
+
`;
|
|
31
|
+
// ─── Loader ─────────────────────────────────────────────────────────────────
|
|
32
|
+
exports.llmLoader = (0, create_loader_1.createModuleLoader)({
|
|
33
|
+
name: 'llm',
|
|
34
|
+
ttlMs: 5 * 60_000,
|
|
35
|
+
async resolve(ctx) {
|
|
36
|
+
const { tenantPool, databaseId } = ctx;
|
|
37
|
+
const result = await tenantPool.query(LLM_MODULE_SQL, [databaseId]);
|
|
38
|
+
const row = result.rows[0];
|
|
39
|
+
if (!row)
|
|
40
|
+
return undefined;
|
|
41
|
+
return {
|
|
42
|
+
embeddingProvider: row.embedding_provider,
|
|
43
|
+
embeddingModel: row.embedding_model,
|
|
44
|
+
embeddingBaseUrl: row.embedding_base_url,
|
|
45
|
+
embeddingDimensions: row.embedding_dimensions,
|
|
46
|
+
chatProvider: row.chat_provider,
|
|
47
|
+
chatModel: row.chat_model,
|
|
48
|
+
chatBaseUrl: row.chat_base_url,
|
|
49
|
+
rateLimitRpm: row.rate_limit_rpm,
|
|
50
|
+
maxTokensPerRequest: row.max_tokens_per_request,
|
|
51
|
+
ragContextLimit: row.rag_context_limit,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/express-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Extractable Express middleware for Constructive tenant context — domain resolution, JWT auth, pgSettings, and withPgClient",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"jwt",
|
|
60
60
|
"pgSettings"
|
|
61
61
|
],
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "16c2ae1c30850db1d71c021346c9d34361361d8e"
|
|
63
63
|
}
|
package/types.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* (PostGraphile, LLM sidecar, etc.) can share the same context.
|
|
8
8
|
*/
|
|
9
9
|
import type { Pool, PoolClient } from 'pg';
|
|
10
|
+
import type { BillingClient } from './billing-client';
|
|
10
11
|
export interface CorsModuleData {
|
|
11
12
|
urls: string[];
|
|
12
13
|
}
|
|
@@ -33,6 +34,7 @@ export interface DatabaseSettings {
|
|
|
33
34
|
enableLlm: boolean;
|
|
34
35
|
enableRealtime: boolean;
|
|
35
36
|
enableBulk: boolean;
|
|
37
|
+
enableI18n: boolean;
|
|
36
38
|
}
|
|
37
39
|
export interface PubkeyChallengeSettings {
|
|
38
40
|
schema: string;
|
|
@@ -135,6 +137,18 @@ export interface AgentChatConfig {
|
|
|
135
137
|
messageTableName: string | null;
|
|
136
138
|
taskTableName: string | null;
|
|
137
139
|
}
|
|
140
|
+
export interface LlmConfig {
|
|
141
|
+
embeddingProvider: string;
|
|
142
|
+
embeddingModel: string;
|
|
143
|
+
embeddingBaseUrl: string;
|
|
144
|
+
embeddingDimensions: number | null;
|
|
145
|
+
chatProvider: string | null;
|
|
146
|
+
chatModel: string | null;
|
|
147
|
+
chatBaseUrl: string | null;
|
|
148
|
+
rateLimitRpm: number | null;
|
|
149
|
+
maxTokensPerRequest: number | null;
|
|
150
|
+
ragContextLimit: number | null;
|
|
151
|
+
}
|
|
138
152
|
/**
|
|
139
153
|
* Maps built-in loader names to their resolved types.
|
|
140
154
|
*
|
|
@@ -153,6 +167,7 @@ export interface BuiltinModuleMap {
|
|
|
153
167
|
billing: BillingConfig;
|
|
154
168
|
inferenceLog: InferenceLogConfig;
|
|
155
169
|
agentChat: AgentChatConfig;
|
|
170
|
+
llm: LlmConfig;
|
|
156
171
|
}
|
|
157
172
|
/**
|
|
158
173
|
* Callback for executing queries within a tenant-scoped transaction
|
|
@@ -202,6 +217,28 @@ export interface ConstructiveContext {
|
|
|
202
217
|
<K extends keyof BuiltinModuleMap>(name: K): Promise<BuiltinModuleMap[K] | undefined>;
|
|
203
218
|
(name: string): Promise<unknown>;
|
|
204
219
|
};
|
|
220
|
+
/**
|
|
221
|
+
* Resolve a shared BillingClient bound to this request's entity.
|
|
222
|
+
*
|
|
223
|
+
* Returns a BillingClient with `checkQuota`, `recordUsage`, and
|
|
224
|
+
* `logInference` methods. Returns null if billing_module is not
|
|
225
|
+
* provisioned for this database or if no entity ID is available.
|
|
226
|
+
*
|
|
227
|
+
* Lazy: only resolves billing/inferenceLog loaders on first call.
|
|
228
|
+
* Cached: subsequent calls return the same client instance.
|
|
229
|
+
*/
|
|
230
|
+
useBilling(): Promise<BillingClient | null>;
|
|
231
|
+
/**
|
|
232
|
+
* Resolve per-database LLM provider config.
|
|
233
|
+
*
|
|
234
|
+
* Returns the resolved LlmConfig from the llm_module table, or null
|
|
235
|
+
* if the module is not provisioned for this database. Callers should
|
|
236
|
+
* fall back to environment variables when null.
|
|
237
|
+
*
|
|
238
|
+
* Lazy: only resolves the llm loader on first call.
|
|
239
|
+
* Cached: subsequent calls return the same config instance.
|
|
240
|
+
*/
|
|
241
|
+
useLlm(): Promise<LlmConfig | null>;
|
|
205
242
|
}
|
|
206
243
|
declare global {
|
|
207
244
|
namespace Express {
|