stellarium-ts 0.14.0 → 0.16.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/dist/client/operatorClient.d.ts +23 -1
- package/dist/client/operatorClient.d.ts.map +1 -1
- package/dist/client/operatorClient.js +1 -1
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +8 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/resource/inference.d.ts +38 -0
- package/dist/resource/inference.d.ts.map +1 -0
- package/dist/resource/inference.js +74 -0
- package/dist/resource/invoker.d.ts +62 -5
- package/dist/resource/invoker.d.ts.map +1 -1
- package/dist/resource/invoker.js +112 -16
- package/dist/resource/repository.d.ts +21 -3
- package/dist/resource/repository.d.ts.map +1 -1
- package/dist/resource/repository.js +25 -4
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +191 -46
- package/dist/server.d.ts +8 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +13 -4
- package/dist/service.d.ts +3 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +1 -1
- package/dist/tests/db/db.test.js +18 -1
- package/dist/tests/helpers.d.ts +9 -0
- package/dist/tests/helpers.d.ts.map +1 -1
- package/dist/tests/helpers.js +35 -2
- package/dist/tests/integration/network.test.js +29 -4
- package/dist/tests/resource/invoker.test.js +89 -3
- package/dist/tests/resource/repository.test.js +19 -5
- package/dist/tests/routes.test.js +143 -5
- package/dist/tests/server.test.js +35 -8
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type PaymentSubmission = FixedPaymentSubmission | InferencePaymentSubmission;
|
|
2
|
+
export interface FixedPaymentSubmission {
|
|
2
3
|
voucher: {
|
|
3
4
|
requestId: string;
|
|
4
5
|
invocationId: string;
|
|
@@ -10,6 +11,27 @@ export interface PaymentSubmission {
|
|
|
10
11
|
publicKey: string;
|
|
11
12
|
};
|
|
12
13
|
}
|
|
14
|
+
/** The provider reports the call's usage; the operator charges it at the voucher's rates, up to `maxAmount`. */
|
|
15
|
+
export interface InferencePaymentSubmission {
|
|
16
|
+
voucher: {
|
|
17
|
+
kind: 'inference';
|
|
18
|
+
requestId: string;
|
|
19
|
+
invocationId: string;
|
|
20
|
+
maxAmount: number;
|
|
21
|
+
inputCostMicroUsdcPerMillionTokens: number;
|
|
22
|
+
outputCostMicroUsdcPerMillionTokens: number;
|
|
23
|
+
maxTokens: number;
|
|
24
|
+
recipientAddress: string;
|
|
25
|
+
signature: string;
|
|
26
|
+
expiresAt: string;
|
|
27
|
+
invocationHash: string;
|
|
28
|
+
publicKey: string;
|
|
29
|
+
};
|
|
30
|
+
usage: {
|
|
31
|
+
inputTokens: number;
|
|
32
|
+
outputTokens: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
13
35
|
export declare const SETTLE_PAYMENT_MAX_RETRIES = 5;
|
|
14
36
|
export declare const SETTLE_PAYMENT_BASE_DELAY_MS = 1000;
|
|
15
37
|
/** Per attempt. A timed-out attempt is retried like a network error. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operatorClient.d.ts","sourceRoot":"","sources":["../../src/client/operatorClient.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"operatorClient.d.ts","sourceRoot":"","sources":["../../src/client/operatorClient.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;AAEpF,MAAM,WAAW,sBAAsB;IACnC,OAAO,EAAE;QACL,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;QACf,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAA;CACJ;AAED,gHAAgH;AAChH,MAAM,WAAW,0BAA0B;IACvC,OAAO,EAAE;QACL,IAAI,EAAE,WAAW,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,kCAAkC,EAAE,MAAM,CAAC;QAC3C,mCAAmC,EAAE,MAAM,CAAC;QAC5C,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACrB,CAAA;IACD,KAAK,EAAE;QACH,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;KACxB,CAAA;CACJ;AAED,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAC5C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,wEAAwE;AACxE,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAmBhD,qBAAa,cAAc;IACX,OAAO,CAAC,QAAQ,CAAC,GAAG;IAAhC,YAA6B,GAAG,EAAE,MAAM,GAAG,SAAS,EAAI;IAExD;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B7D;CACJ"}
|
|
@@ -4,7 +4,7 @@ export const SETTLE_PAYMENT_BASE_DELAY_MS = 1000;
|
|
|
4
4
|
export const SETTLE_PAYMENT_TIMEOUT_MS = 10_000;
|
|
5
5
|
/**
|
|
6
6
|
* Transient failures. Resending is safe even if an earlier attempt reached the operator: it stores a
|
|
7
|
-
* unique receipt per (requestId, invocationId)
|
|
7
|
+
* unique receipt per (requestId, invocationId), so a repeat is rejected with 409.
|
|
8
8
|
* Rejections (400, 401, 404, 409, 422) are not retried because they won't change.
|
|
9
9
|
*/
|
|
10
10
|
const RETRYABLE_STATUSES = new Set([408, 429, 500, 502, 503, 504]);
|
package/dist/db/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE7C,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE7C,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAY1D,wGAAwG;AACxG,wBAAsB,YAAY,CAAC,QAAQ,GAAE,MAA8B,GAAG,OAAO,CAAC,QAAQ,CAAC,CAI9F;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,QAAQ,iBA8I9C"}
|
package/dist/db/db.js
CHANGED
|
@@ -6,8 +6,10 @@ export const DEFAULT_DATABASE_PATH = './requestscript.db';
|
|
|
6
6
|
* Version 1 dropped `UNIQUE (base_url)` from `resource` and added uniqueness to the function tables.
|
|
7
7
|
* Version 2 added `resource_function.restricted`.
|
|
8
8
|
* Version 3 added `resource.website` and the `description` columns of the resource tables.
|
|
9
|
+
* Version 4 renamed `resource_function_payment_manifest.cost_lamports` to `cost_micro_usdc` (payments moved to USDC).
|
|
10
|
+
* Version 5 added `resource_function.kind` and per-token inference pricing, which made `cost_micro_usdc` nullable.
|
|
9
11
|
*/
|
|
10
|
-
const SCHEMA_VERSION =
|
|
12
|
+
const SCHEMA_VERSION = 5;
|
|
11
13
|
/** Opens (or creates) the node database and brings its schema up to date. Pass ':memory:' for tests. */
|
|
12
14
|
export async function openDatabase(filename = DEFAULT_DATABASE_PATH) {
|
|
13
15
|
const db = await open({ filename, driver: sqlite3.Database });
|
|
@@ -63,6 +65,7 @@ export async function createTables(db) {
|
|
|
63
65
|
return_type TEXT NOT NULL,
|
|
64
66
|
description TEXT,
|
|
65
67
|
restricted INTEGER NOT NULL DEFAULT 0,
|
|
68
|
+
kind TEXT NOT NULL DEFAULT 'fixed',
|
|
66
69
|
created_at TEXT NOT NULL,
|
|
67
70
|
updated_at TEXT NOT NULL,
|
|
68
71
|
|
|
@@ -88,7 +91,10 @@ export async function createTables(db) {
|
|
|
88
91
|
CREATE TABLE IF NOT EXISTS resource_function_payment_manifest (
|
|
89
92
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
90
93
|
resource_function_id INTEGER NOT NULL,
|
|
91
|
-
|
|
94
|
+
-- Null for inference functions, which are priced per million tokens instead.
|
|
95
|
+
cost_micro_usdc INTEGER,
|
|
96
|
+
input_cost_micro_usdc_per_million_tokens INTEGER,
|
|
97
|
+
output_cost_micro_usdc_per_million_tokens INTEGER,
|
|
92
98
|
recipient_address TEXT NOT NULL,
|
|
93
99
|
created_at TEXT NOT NULL,
|
|
94
100
|
updated_at TEXT NOT NULL,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { StellariumNode, resolveConfig, type StellariumNodeOptions } from './server.js';
|
|
2
2
|
export type { NodeConfig } from './service.js';
|
|
3
|
-
export type { StellariumResource, StellariumResourceFunction, StellariumResourceFunctionParameter, StellariumResourceFunctionPaymentManifest } from './resource/repository.js';
|
|
4
|
-
export { CombinationResourceInvoker, invocationHash, paymentVoucherPayload, type PaymentVoucher } from './resource/invoker.js';
|
|
3
|
+
export type { StellariumFixedPaymentManifest, StellariumInferencePaymentManifest, StellariumResource, StellariumResourceFunction, StellariumResourceFunctionKind, StellariumResourceFunctionParameter, StellariumResourceFunctionPaymentManifest, } from './resource/repository.js';
|
|
4
|
+
export { CombinationResourceInvoker, inferenceVoucherPayload, invocationHash, paymentVoucherPayload, type InferenceEstimate, type InferencePaymentVoucher, type PaymentVoucher, } from './resource/invoker.js';
|
|
5
|
+
export { inferenceCharge, MAX_TOKENS_PARAMETER, type InferenceResult, type InferenceUsage } from './resource/inference.js';
|
|
6
|
+
export type { InferencePaymentSubmission, PaymentSubmission } from './client/operatorClient.js';
|
|
5
7
|
export { grantHash, grantPayload, grantRenewalPayload, grantRequestPayload, grantsHash, CLOCK_SKEW_TOLERANCE_MS, invocationAuthorizationPayload, MAX_AUTHORIZATION_LIFETIME_MS, MAX_GRANT_CHAIN_LENGTH, MAX_GRANT_LIFETIME_MS, type Capability, type Grant, type GrantRenewalRequest, type GrantRequest, type InvocationAuthorization, } from './grant/grant.js';
|
|
6
8
|
export type { ApproveGrant, GrantApproval, GrantOptions } from './grant/service.js';
|
|
7
9
|
export type { GrantKey } from './grant/repository.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACR,8BAA8B,EAC9B,kCAAkC,EAClC,kBAAkB,EAClB,0BAA0B,EAC1B,8BAA8B,EAC9B,mCAAmC,EACnC,yCAAyC,GAC5C,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACH,0BAA0B,EAC1B,uBAAuB,EACvB,cAAc,EACd,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,cAAc,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC3H,YAAY,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEhG,OAAO,EACH,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,uBAAuB,GAC/B,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { StellariumNode, resolveConfig } from './server.js';
|
|
2
2
|
// The voucher wire format, so the operator and other settlers can test against the real signer.
|
|
3
|
-
export { CombinationResourceInvoker, invocationHash, paymentVoucherPayload } from './resource/invoker.js';
|
|
3
|
+
export { CombinationResourceInvoker, inferenceVoucherPayload, invocationHash, paymentVoucherPayload, } from './resource/invoker.js';
|
|
4
|
+
export { inferenceCharge, MAX_TOKENS_PARAMETER } from './resource/inference.js';
|
|
4
5
|
// Grant wire format, so other implementations can test against the real signer.
|
|
5
6
|
export { grantHash, grantPayload, grantRenewalPayload, grantRequestPayload, grantsHash, CLOCK_SKEW_TOLERANCE_MS, invocationAuthorizationPayload, MAX_AUTHORIZATION_LIFETIME_MS, MAX_GRANT_CHAIN_LENGTH, MAX_GRANT_LIFETIME_MS, } from './grant/grant.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ResourceFunctionCallParameter } from "requestscript";
|
|
2
|
+
import type { StellariumInferencePaymentManifest, StellariumResourceFunction, StellariumResourceFunctionPaymentManifest } from "./repository.js";
|
|
3
|
+
/** The parameter every inference function takes: the most output tokens the caller allows. */
|
|
4
|
+
export declare const MAX_TOKENS_PARAMETER = "maxTokens";
|
|
5
|
+
/** Token counts an inference function reports for one call. */
|
|
6
|
+
export interface InferenceUsage {
|
|
7
|
+
inputTokens: number;
|
|
8
|
+
outputTokens: number;
|
|
9
|
+
}
|
|
10
|
+
/** What an inference function's `exec` returns. Callers' scripts see only `value`. */
|
|
11
|
+
export interface InferenceResult {
|
|
12
|
+
value: unknown;
|
|
13
|
+
usage: InferenceUsage;
|
|
14
|
+
}
|
|
15
|
+
/** The per-million-token rates a charge is computed from. */
|
|
16
|
+
export interface InferenceRates {
|
|
17
|
+
inputCostMicroUsdcPerMillionTokens: number;
|
|
18
|
+
outputCostMicroUsdcPerMillionTokens: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function isInferenceManifest(manifest: StellariumResourceFunctionPaymentManifest | undefined): manifest is StellariumInferencePaymentManifest;
|
|
21
|
+
export declare function isInferenceFunction(fn: Pick<StellariumResourceFunction, 'kind'>): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Micro-USDC owed for `inputTokens` and `outputTokens` at `rates`, rounded up.
|
|
24
|
+
* Computed exactly in BigInt. Throws if the charge isn't a safe integer.
|
|
25
|
+
* The operator keeps a copy (apps/operator/src/inference.ts) that must stay in sync.
|
|
26
|
+
*/
|
|
27
|
+
export declare function inferenceCharge(rates: InferenceRates, inputTokens: number, outputTokens: number): number;
|
|
28
|
+
/** The call's `maxTokens` value, or undefined unless it is a positive int32. */
|
|
29
|
+
export declare function parseMaxTokens(parameters: ResourceFunctionCallParameter[]): number | undefined;
|
|
30
|
+
/** The usage an inference `exec` reported, or undefined if it isn't two nonnegative safe integers. */
|
|
31
|
+
export declare function parseInferenceResult(result: unknown): InferenceResult | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Throws unless an inference function declares an int32 `maxTokens` parameter and, when paid,
|
|
34
|
+
* an inference payment manifest with nonnegative integer rates and an `estimateInputTokens` function.
|
|
35
|
+
* A function that isn't an inference function may not carry an inference manifest.
|
|
36
|
+
*/
|
|
37
|
+
export declare function validateInferenceFunction(fn: StellariumResourceFunction): void;
|
|
38
|
+
//# sourceMappingURL=inference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/resource/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,kCAAkC,EAAE,0BAA0B,EAAE,yCAAyC,EAAE,MAAM,iBAAiB,CAAC;AAEjJ,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAIhD,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;CACzB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC3B,kCAAkC,EAAE,MAAM,CAAC;IAC3C,mCAAmC,EAAE,MAAM,CAAC;CAC/C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,yCAAyC,GAAG,SAAS,GAAG,QAAQ,IAAI,kCAAkC,CAEnJ;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,GAAG,OAAO,CAEzF;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAQxG;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,6BAA6B,EAAE,GAAG,MAAM,GAAG,SAAS,CAG9F;AAED,sGAAsG;AACtG,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAajF;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,0BAA0B,GAAG,IAAI,CAsB9E"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** The parameter every inference function takes: the most output tokens the caller allows. */
|
|
2
|
+
export const MAX_TOKENS_PARAMETER = 'maxTokens';
|
|
3
|
+
const INT32_MAX = 2_147_483_647;
|
|
4
|
+
export function isInferenceManifest(manifest) {
|
|
5
|
+
return typeof manifest === 'object' && manifest !== null && 'inputCostMicroUsdcPerMillionTokens' in manifest;
|
|
6
|
+
}
|
|
7
|
+
export function isInferenceFunction(fn) {
|
|
8
|
+
return fn.kind === 'inference';
|
|
9
|
+
}
|
|
10
|
+
function isNonnegativeSafeInteger(value) {
|
|
11
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Micro-USDC owed for `inputTokens` and `outputTokens` at `rates`, rounded up.
|
|
15
|
+
* Computed exactly in BigInt. Throws if the charge isn't a safe integer.
|
|
16
|
+
* The operator keeps a copy (apps/operator/src/inference.ts) that must stay in sync.
|
|
17
|
+
*/
|
|
18
|
+
export function inferenceCharge(rates, inputTokens, outputTokens) {
|
|
19
|
+
const micros = BigInt(inputTokens) * BigInt(rates.inputCostMicroUsdcPerMillionTokens)
|
|
20
|
+
+ BigInt(outputTokens) * BigInt(rates.outputCostMicroUsdcPerMillionTokens);
|
|
21
|
+
const charge = (micros + 999999n) / 1000000n;
|
|
22
|
+
if (charge > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
23
|
+
throw new Error('Inference charge exceeds the largest safe integer');
|
|
24
|
+
}
|
|
25
|
+
return Number(charge);
|
|
26
|
+
}
|
|
27
|
+
/** The call's `maxTokens` value, or undefined unless it is a positive int32. */
|
|
28
|
+
export function parseMaxTokens(parameters) {
|
|
29
|
+
const value = parameters.find(parameter => parameter.name === MAX_TOKENS_PARAMETER)?.value;
|
|
30
|
+
return Number.isInteger(value) && value > 0 && value <= INT32_MAX ? value : undefined;
|
|
31
|
+
}
|
|
32
|
+
/** The usage an inference `exec` reported, or undefined if it isn't two nonnegative safe integers. */
|
|
33
|
+
export function parseInferenceResult(result) {
|
|
34
|
+
if (typeof result !== 'object' || result === null || !('usage' in result)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const { value, usage } = result;
|
|
38
|
+
if (typeof usage !== 'object' || usage === null) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const { inputTokens, outputTokens } = usage;
|
|
42
|
+
if (!isNonnegativeSafeInteger(inputTokens) || !isNonnegativeSafeInteger(outputTokens)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return { value, usage: { inputTokens, outputTokens } };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Throws unless an inference function declares an int32 `maxTokens` parameter and, when paid,
|
|
49
|
+
* an inference payment manifest with nonnegative integer rates and an `estimateInputTokens` function.
|
|
50
|
+
* A function that isn't an inference function may not carry an inference manifest.
|
|
51
|
+
*/
|
|
52
|
+
export function validateInferenceFunction(fn) {
|
|
53
|
+
if (!isInferenceFunction(fn)) {
|
|
54
|
+
if (isInferenceManifest(fn.paymentManifest)) {
|
|
55
|
+
throw new Error(`Function ${fn.name} has per-token pricing but is not declared with kind 'inference'`);
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const maxTokens = fn.parameters.find(parameter => parameter.name === MAX_TOKENS_PARAMETER);
|
|
60
|
+
if (maxTokens?.type !== 'int32') {
|
|
61
|
+
throw new Error(`Inference function ${fn.name} must take a '${MAX_TOKENS_PARAMETER}: int32' parameter`);
|
|
62
|
+
}
|
|
63
|
+
if (!fn.paymentManifest) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!isInferenceManifest(fn.paymentManifest)
|
|
67
|
+
|| !isNonnegativeSafeInteger(fn.paymentManifest.inputCostMicroUsdcPerMillionTokens)
|
|
68
|
+
|| !isNonnegativeSafeInteger(fn.paymentManifest.outputCostMicroUsdcPerMillionTokens)) {
|
|
69
|
+
throw new Error(`Inference function ${fn.name} must price input and output tokens as nonnegative integer micro-USDC per million tokens`);
|
|
70
|
+
}
|
|
71
|
+
if (typeof fn.estimateInputTokens !== 'function') {
|
|
72
|
+
throw new Error(`Paid inference function ${fn.name} must declare estimateInputTokens`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Resource, ResourceFunctionCallParameter, ResourceInvoker } from "requestscript";
|
|
2
2
|
import type { GrantService } from "../grant/service.js";
|
|
3
|
-
import {
|
|
3
|
+
import { type InferenceUsage } from "./inference.js";
|
|
4
|
+
import { ResourceRepository, type StellariumInferencePaymentManifest } from "./repository.js";
|
|
4
5
|
export interface PaymentVoucher {
|
|
5
6
|
requestId: string;
|
|
6
7
|
invocationId: string;
|
|
@@ -11,6 +12,30 @@ export interface PaymentVoucher {
|
|
|
11
12
|
invocationHash: string;
|
|
12
13
|
publicKey: string;
|
|
13
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* A voucher for an inference function. It signs the provider's per-million-token rates and the call's
|
|
17
|
+
* `maxTokens` instead of an amount: the operator charges the usage the provider reports at those rates,
|
|
18
|
+
* up to `maxAmount`.
|
|
19
|
+
*/
|
|
20
|
+
export interface InferencePaymentVoucher {
|
|
21
|
+
kind: 'inference';
|
|
22
|
+
requestId: string;
|
|
23
|
+
invocationId: string;
|
|
24
|
+
maxAmount: number;
|
|
25
|
+
inputCostMicroUsdcPerMillionTokens: number;
|
|
26
|
+
outputCostMicroUsdcPerMillionTokens: number;
|
|
27
|
+
maxTokens: number;
|
|
28
|
+
recipientAddress: string;
|
|
29
|
+
signature: string;
|
|
30
|
+
expiresAt: string;
|
|
31
|
+
invocationHash: string;
|
|
32
|
+
publicKey: string;
|
|
33
|
+
}
|
|
34
|
+
/** What a provider's `POST /v1/run/resource/estimate` answers. */
|
|
35
|
+
export interface InferenceEstimate {
|
|
36
|
+
inputTokens: number;
|
|
37
|
+
maxAmount: number;
|
|
38
|
+
}
|
|
14
39
|
/**
|
|
15
40
|
* Hash binding a voucher to one function call. Providers recompute it from the
|
|
16
41
|
* request they receive and compare it to `voucher.invocationHash`.
|
|
@@ -30,24 +55,52 @@ export declare function paymentVoucherPayload(voucher: Omit<PaymentVoucher, "sig
|
|
|
30
55
|
invocationHash: string;
|
|
31
56
|
publicKey: string;
|
|
32
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
|
|
60
|
+
* Key order is part of the signed bytes.
|
|
61
|
+
*/
|
|
62
|
+
export declare function inferenceVoucherPayload(voucher: Omit<InferencePaymentVoucher, "signature">): {
|
|
63
|
+
kind: "inference";
|
|
64
|
+
requestId: string;
|
|
65
|
+
invocationId: string;
|
|
66
|
+
maxAmount: number;
|
|
67
|
+
inputCostMicroUsdcPerMillionTokens: number;
|
|
68
|
+
outputCostMicroUsdcPerMillionTokens: number;
|
|
69
|
+
maxTokens: number;
|
|
70
|
+
recipientAddress: string;
|
|
71
|
+
expiresAt: string;
|
|
72
|
+
invocationHash: string;
|
|
73
|
+
publicKey: string;
|
|
74
|
+
};
|
|
75
|
+
export declare function isInferenceVoucher(voucher: PaymentVoucher | InferencePaymentVoucher): voucher is InferencePaymentVoucher;
|
|
76
|
+
export declare const PAYMENT_VOUCHER_LIFETIME_MS: number;
|
|
77
|
+
/** Inference can run for minutes, and the operator refuses a voucher that expired before the provider settled it. */
|
|
78
|
+
export declare const INFERENCE_VOUCHER_LIFETIME_MS: number;
|
|
33
79
|
export declare const EXTERNAL_INVOKE_MAX_RETRIES = 3;
|
|
34
80
|
export declare const EXTERNAL_INVOKE_BASE_DELAY_MS = 500;
|
|
35
81
|
/** Per attempt. A timed-out attempt is retried like a network error. */
|
|
36
82
|
export declare const EXTERNAL_INVOKE_TIMEOUT_MS = 30000;
|
|
83
|
+
/** Per attempt, for calls to inference functions. Every attempt fits within `INFERENCE_VOUCHER_LIFETIME_MS`. */
|
|
84
|
+
export declare const INFERENCE_INVOKE_TIMEOUT_MS = 120000;
|
|
37
85
|
export declare class CombinationResourceInvoker implements ResourceInvoker {
|
|
38
86
|
private readonly resourceRepository;
|
|
39
|
-
private readonly
|
|
87
|
+
private readonly costLimitMicroUsdc;
|
|
40
88
|
private readonly publicKey;
|
|
41
89
|
private readonly privateKey;
|
|
42
90
|
private readonly grantService?;
|
|
43
|
-
|
|
44
|
-
|
|
91
|
+
private readonly inferenceCostLimitMicroUsdc;
|
|
92
|
+
/**
|
|
93
|
+
* Without a `grantService`, calls to restricted remote functions fail.
|
|
94
|
+
* `inferenceCostLimitMicroUsdc` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsdc`.
|
|
95
|
+
*/
|
|
96
|
+
constructor(resourceRepository: ResourceRepository, costLimitMicroUsdc: number, publicKey: string, privateKey: string, grantService?: GrantService | undefined, inferenceCostLimitMicroUsdc?: number);
|
|
45
97
|
/**
|
|
46
98
|
* Invoke a function on a resource.
|
|
47
99
|
* If the resource has a baseUrl, it will be invoked as a remote function.
|
|
48
100
|
* If the function is restricted, it attaches an authorization from a held grant.
|
|
49
101
|
* If the resource has a paymentManifest, it will create a payment voucher and pass it to the resource.
|
|
50
|
-
*
|
|
102
|
+
* A paid inference function is first priced by the provider's estimate endpoint.
|
|
103
|
+
* Otherwise, it will be invoked as a local function. A local inference function's usage is returned beside its value.
|
|
51
104
|
*
|
|
52
105
|
* @param resource - The resource to invoke
|
|
53
106
|
* @param functionName - The name of the function to invoke
|
|
@@ -59,7 +112,11 @@ export declare class CombinationResourceInvoker implements ResourceInvoker {
|
|
|
59
112
|
error: {
|
|
60
113
|
message: string;
|
|
61
114
|
} | null;
|
|
115
|
+
usage?: InferenceUsage;
|
|
62
116
|
}>;
|
|
117
|
+
/** Asks the provider what a call to a paid inference function will cost at most. */
|
|
118
|
+
private estimate;
|
|
63
119
|
createPaymentVoucher(recipientAddress: string, amount: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[]): PaymentVoucher;
|
|
120
|
+
createInferenceVoucher(manifest: StellariumInferencePaymentManifest, maxAmount: number, maxTokens: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[]): InferencePaymentVoucher;
|
|
64
121
|
}
|
|
65
122
|
//# sourceMappingURL=invoker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/resource/invoker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"invoker.d.ts","sourceRoot":"","sources":["../../src/resource/invoker.ts"],"names":[],"mappings":"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"}
|
package/dist/resource/invoker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hashData, signData } from "../crypto/utils.js";
|
|
2
|
+
import { inferenceCharge, isInferenceFunction, isInferenceManifest, parseInferenceResult, parseMaxTokens } from "./inference.js";
|
|
2
3
|
/**
|
|
3
4
|
* Hash binding a voucher to one function call. Providers recompute it from the
|
|
4
5
|
* request they receive and compare it to `voucher.invocationHash`.
|
|
@@ -15,20 +16,36 @@ export function paymentVoucherPayload(voucher) {
|
|
|
15
16
|
const { requestId, invocationId, amount, recipientAddress, expiresAt, invocationHash, publicKey } = voucher;
|
|
16
17
|
return { requestId, invocationId, amount, recipientAddress, expiresAt, invocationHash, publicKey };
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
|
|
21
|
+
* Key order is part of the signed bytes.
|
|
22
|
+
*/
|
|
23
|
+
export function inferenceVoucherPayload(voucher) {
|
|
24
|
+
const { kind, requestId, invocationId, maxAmount, inputCostMicroUsdcPerMillionTokens, outputCostMicroUsdcPerMillionTokens, maxTokens, recipientAddress, expiresAt, invocationHash, publicKey } = voucher;
|
|
25
|
+
return { kind, requestId, invocationId, maxAmount, inputCostMicroUsdcPerMillionTokens, outputCostMicroUsdcPerMillionTokens, maxTokens, recipientAddress, expiresAt, invocationHash, publicKey };
|
|
26
|
+
}
|
|
27
|
+
export function isInferenceVoucher(voucher) {
|
|
28
|
+
return voucher.kind === 'inference';
|
|
29
|
+
}
|
|
30
|
+
export const PAYMENT_VOUCHER_LIFETIME_MS = 1000 * 60 * 2;
|
|
31
|
+
/** Inference can run for minutes, and the operator refuses a voucher that expired before the provider settled it. */
|
|
32
|
+
export const INFERENCE_VOUCHER_LIFETIME_MS = 1000 * 60 * 10;
|
|
18
33
|
export const EXTERNAL_INVOKE_MAX_RETRIES = 3;
|
|
19
34
|
export const EXTERNAL_INVOKE_BASE_DELAY_MS = 500;
|
|
20
35
|
/** Per attempt. A timed-out attempt is retried like a network error. */
|
|
21
36
|
export const EXTERNAL_INVOKE_TIMEOUT_MS = 30_000;
|
|
37
|
+
/** Per attempt, for calls to inference functions. Every attempt fits within `INFERENCE_VOUCHER_LIFETIME_MS`. */
|
|
38
|
+
export const INFERENCE_INVOKE_TIMEOUT_MS = 120_000;
|
|
22
39
|
/**
|
|
23
40
|
* POST to a remote resource, retrying network errors, 429s, and 5xx responses up to
|
|
24
41
|
* `EXTERNAL_INVOKE_MAX_RETRIES` times with exponential backoff (500ms, 1s, 2s).
|
|
25
42
|
* Other 4xx responses are returned immediately since retrying won't change them.
|
|
26
43
|
* Throws the last network error if every attempt fails to connect.
|
|
27
44
|
*/
|
|
28
|
-
async function fetchWithRetry(url, init) {
|
|
45
|
+
async function fetchWithRetry(url, init, timeoutMs = EXTERNAL_INVOKE_TIMEOUT_MS) {
|
|
29
46
|
for (let attempt = 0;; attempt++) {
|
|
30
47
|
try {
|
|
31
|
-
const response = await fetch(url, { ...init(), signal: AbortSignal.timeout(
|
|
48
|
+
const response = await fetch(url, { ...init(), signal: AbortSignal.timeout(timeoutMs) });
|
|
32
49
|
if (response.status !== 429 && response.status < 500) {
|
|
33
50
|
return response;
|
|
34
51
|
}
|
|
@@ -46,24 +63,30 @@ async function fetchWithRetry(url, init) {
|
|
|
46
63
|
}
|
|
47
64
|
export class CombinationResourceInvoker {
|
|
48
65
|
resourceRepository;
|
|
49
|
-
|
|
66
|
+
costLimitMicroUsdc;
|
|
50
67
|
publicKey;
|
|
51
68
|
privateKey;
|
|
52
69
|
grantService;
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
inferenceCostLimitMicroUsdc;
|
|
71
|
+
/**
|
|
72
|
+
* Without a `grantService`, calls to restricted remote functions fail.
|
|
73
|
+
* `inferenceCostLimitMicroUsdc` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsdc`.
|
|
74
|
+
*/
|
|
75
|
+
constructor(resourceRepository, costLimitMicroUsdc, publicKey, privateKey, grantService, inferenceCostLimitMicroUsdc = costLimitMicroUsdc) {
|
|
55
76
|
this.resourceRepository = resourceRepository;
|
|
56
|
-
this.
|
|
77
|
+
this.costLimitMicroUsdc = costLimitMicroUsdc;
|
|
57
78
|
this.publicKey = publicKey;
|
|
58
79
|
this.privateKey = privateKey;
|
|
59
80
|
this.grantService = grantService;
|
|
81
|
+
this.inferenceCostLimitMicroUsdc = inferenceCostLimitMicroUsdc;
|
|
60
82
|
}
|
|
61
83
|
/**
|
|
62
84
|
* Invoke a function on a resource.
|
|
63
85
|
* If the resource has a baseUrl, it will be invoked as a remote function.
|
|
64
86
|
* If the function is restricted, it attaches an authorization from a held grant.
|
|
65
87
|
* If the resource has a paymentManifest, it will create a payment voucher and pass it to the resource.
|
|
66
|
-
*
|
|
88
|
+
* A paid inference function is first priced by the provider's estimate endpoint.
|
|
89
|
+
* Otherwise, it will be invoked as a local function. A local inference function's usage is returned beside its value.
|
|
67
90
|
*
|
|
68
91
|
* @param resource - The resource to invoke
|
|
69
92
|
* @param functionName - The name of the function to invoke
|
|
@@ -92,14 +115,41 @@ export class CombinationResourceInvoker {
|
|
|
92
115
|
if (resourceFunction.restricted && !chain) {
|
|
93
116
|
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: it requires a grant from ${url}, and this node holds none that covers the call` } };
|
|
94
117
|
}
|
|
95
|
-
|
|
96
|
-
|
|
118
|
+
const manifest = resourceFunction.paymentManifest;
|
|
119
|
+
const inference = isInferenceFunction(resourceFunction);
|
|
120
|
+
if (manifest && inference) {
|
|
121
|
+
console.log(`Resource function ${resource.name}.${functionName} is a paid inference function, creating payment voucher...`);
|
|
122
|
+
// Peers' manifests aren't validated on receipt.
|
|
123
|
+
if (!isInferenceManifest(manifest) || !Number.isSafeInteger(manifest.inputCostMicroUsdcPerMillionTokens) || manifest.inputCostMicroUsdcPerMillionTokens < 0
|
|
124
|
+
|| !Number.isSafeInteger(manifest.outputCostMicroUsdcPerMillionTokens) || manifest.outputCostMicroUsdcPerMillionTokens < 0) {
|
|
125
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: its payment manifest has no valid per-token pricing` } };
|
|
126
|
+
}
|
|
127
|
+
const maxTokens = parseMaxTokens(parameters);
|
|
128
|
+
if (maxTokens === undefined) {
|
|
129
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: maxTokens must be a positive int32` } };
|
|
130
|
+
}
|
|
131
|
+
const estimate = await this.estimate(url, target, chain);
|
|
132
|
+
if ('error' in estimate) {
|
|
133
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: ${estimate.error}` } };
|
|
134
|
+
}
|
|
135
|
+
// The provider may pick the input estimate, but maxAmount must follow from it at the advertised rates.
|
|
136
|
+
if (inferenceCharge(manifest, estimate.inputTokens, maxTokens) !== estimate.maxAmount) {
|
|
137
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: the provider's estimate doesn't match its advertised rates` } };
|
|
138
|
+
}
|
|
139
|
+
if (estimate.maxAmount > this.inferenceCostLimitMicroUsdc) {
|
|
140
|
+
throw new Error(`Function ${functionName} may cost up to ${estimate.maxAmount} micro-USDC, which exceeds the inference cost limit of ${this.inferenceCostLimitMicroUsdc} micro-USDC`);
|
|
141
|
+
}
|
|
142
|
+
requestBody.paymentVoucher = this.createInferenceVoucher(manifest, estimate.maxAmount, maxTokens, resource.path, resource.name, functionName, parameters);
|
|
143
|
+
}
|
|
144
|
+
else if (manifest) {
|
|
97
145
|
console.log(`Resource function ${resource.name}.${functionName} has a payment manifest, creating payment voucher...`);
|
|
98
|
-
if (
|
|
99
|
-
|
|
146
|
+
if (isInferenceManifest(manifest)) {
|
|
147
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: it has per-token pricing but isn't an inference function` } };
|
|
148
|
+
}
|
|
149
|
+
if (manifest.costMicroUsdc > this.costLimitMicroUsdc) {
|
|
150
|
+
throw new Error(`Function ${functionName} cost ${manifest.costMicroUsdc} micro-USDC, which exceeds the cost limit of ${this.costLimitMicroUsdc} micro-USDC`);
|
|
100
151
|
}
|
|
101
|
-
paymentVoucher = this.createPaymentVoucher(
|
|
102
|
-
requestBody.paymentVoucher = paymentVoucher;
|
|
152
|
+
requestBody.paymentVoucher = this.createPaymentVoucher(manifest.recipientAddress, manifest.costMicroUsdc, resource.path, resource.name, functionName, parameters);
|
|
103
153
|
}
|
|
104
154
|
// The voucher is created once and reused on every attempt, so the operator's
|
|
105
155
|
// requestId/invocationId check prevents a retried call from being settled twice.
|
|
@@ -110,7 +160,7 @@ export class CombinationResourceInvoker {
|
|
|
110
160
|
headers: {
|
|
111
161
|
'Content-Type': 'application/json',
|
|
112
162
|
},
|
|
113
|
-
}));
|
|
163
|
+
}), inference ? INFERENCE_INVOKE_TIMEOUT_MS : EXTERNAL_INVOKE_TIMEOUT_MS);
|
|
114
164
|
if (!result.ok) {
|
|
115
165
|
// The provider's JSON error is more useful than the status text, which can be empty.
|
|
116
166
|
const detail = await result.json().then((body) => body?.error, () => undefined);
|
|
@@ -126,16 +176,62 @@ export class CombinationResourceInvoker {
|
|
|
126
176
|
return { value: null, error: { message: `Function ${functionName} not found in resource ${resource.name}` } };
|
|
127
177
|
}
|
|
128
178
|
const result = await fn.exec(parameters);
|
|
179
|
+
if (isInferenceFunction(fn)) {
|
|
180
|
+
const inferenceResult = parseInferenceResult(result);
|
|
181
|
+
if (!inferenceResult) {
|
|
182
|
+
return { value: null, error: { message: `Inference function ${functionName} must return { value, usage: { inputTokens, outputTokens } }` } };
|
|
183
|
+
}
|
|
184
|
+
return { value: inferenceResult.value, error: null, usage: inferenceResult.usage };
|
|
185
|
+
}
|
|
129
186
|
return { value: result, error: null };
|
|
130
187
|
}
|
|
131
188
|
}
|
|
189
|
+
/** Asks the provider what a call to a paid inference function will cost at most. */
|
|
190
|
+
async estimate(url, target, chain) {
|
|
191
|
+
let response;
|
|
192
|
+
try {
|
|
193
|
+
// The authorization is signed per attempt, since the provider refuses a reused nonce.
|
|
194
|
+
response = await fetchWithRetry(`${url}/v1/run/resource/estimate`, () => ({
|
|
195
|
+
method: 'POST',
|
|
196
|
+
body: JSON.stringify(chain && this.grantService ? { ...target, authorization: this.grantService.authorize(chain, target) } : target),
|
|
197
|
+
headers: { 'Content-Type': 'application/json' },
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return { error: `estimate failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
202
|
+
}
|
|
203
|
+
const body = await response.json().catch(() => undefined);
|
|
204
|
+
if (!response.ok) {
|
|
205
|
+
return { error: `estimate failed: ${typeof body?.error === 'string' ? body.error : `${response.status} ${response.statusText}`.trim()}` };
|
|
206
|
+
}
|
|
207
|
+
const { inputTokens, maxAmount } = body ?? {};
|
|
208
|
+
if (!Number.isSafeInteger(inputTokens) || inputTokens < 0 || !Number.isSafeInteger(maxAmount) || maxAmount < 0) {
|
|
209
|
+
return { error: 'the provider returned an invalid estimate' };
|
|
210
|
+
}
|
|
211
|
+
return { inputTokens: inputTokens, maxAmount: maxAmount };
|
|
212
|
+
}
|
|
132
213
|
createPaymentVoucher(recipientAddress, amount, resourcePath, resourceName, functionName, parameters) {
|
|
133
214
|
const requestId = crypto.randomUUID();
|
|
134
215
|
const invocationId = crypto.randomUUID();
|
|
135
|
-
|
|
136
|
-
const expiresAt = new Date(Date.now() + 1000 * 60 * 2).toISOString();
|
|
216
|
+
const expiresAt = new Date(Date.now() + PAYMENT_VOUCHER_LIFETIME_MS).toISOString();
|
|
137
217
|
const hash = invocationHash(resourcePath, resourceName, functionName, parameters);
|
|
138
218
|
const signature = signData(paymentVoucherPayload({ requestId, invocationId, amount, recipientAddress, expiresAt, invocationHash: hash, publicKey: this.publicKey }), this.privateKey);
|
|
139
219
|
return { requestId, invocationId, amount, recipientAddress, signature, expiresAt, invocationHash: hash, publicKey: this.publicKey };
|
|
140
220
|
}
|
|
221
|
+
createInferenceVoucher(manifest, maxAmount, maxTokens, resourcePath, resourceName, functionName, parameters) {
|
|
222
|
+
const unsigned = {
|
|
223
|
+
kind: 'inference',
|
|
224
|
+
requestId: crypto.randomUUID(),
|
|
225
|
+
invocationId: crypto.randomUUID(),
|
|
226
|
+
maxAmount,
|
|
227
|
+
inputCostMicroUsdcPerMillionTokens: manifest.inputCostMicroUsdcPerMillionTokens,
|
|
228
|
+
outputCostMicroUsdcPerMillionTokens: manifest.outputCostMicroUsdcPerMillionTokens,
|
|
229
|
+
maxTokens,
|
|
230
|
+
recipientAddress: manifest.recipientAddress,
|
|
231
|
+
expiresAt: new Date(Date.now() + INFERENCE_VOUCHER_LIFETIME_MS).toISOString(),
|
|
232
|
+
invocationHash: invocationHash(resourcePath, resourceName, functionName, parameters),
|
|
233
|
+
publicKey: this.publicKey,
|
|
234
|
+
};
|
|
235
|
+
return { ...unsigned, signature: signData(inferenceVoucherPayload(unsigned), this.privateKey) };
|
|
236
|
+
}
|
|
141
237
|
}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import { Resource, ResourceFunction, ResourceFunctionParameter } from "requestscript";
|
|
1
|
+
import { Resource, ResourceFunction, ResourceFunctionCallParameter, ResourceFunctionParameter } from "requestscript";
|
|
2
2
|
import { Database } from "sqlite";
|
|
3
3
|
import type { StoredPeer } from "../peer/repository.js";
|
|
4
|
-
|
|
4
|
+
/** A flat price per call. */
|
|
5
|
+
export interface StellariumFixedPaymentManifest {
|
|
5
6
|
id?: number;
|
|
6
|
-
|
|
7
|
+
costMicroUsdc: number;
|
|
7
8
|
recipientAddress: string;
|
|
8
9
|
}
|
|
10
|
+
/** Per-token pricing for an inference function. Either rate may be 0. */
|
|
11
|
+
export interface StellariumInferencePaymentManifest {
|
|
12
|
+
id?: number;
|
|
13
|
+
inputCostMicroUsdcPerMillionTokens: number;
|
|
14
|
+
outputCostMicroUsdcPerMillionTokens: number;
|
|
15
|
+
recipientAddress: string;
|
|
16
|
+
}
|
|
17
|
+
export type StellariumResourceFunctionPaymentManifest = StellariumFixedPaymentManifest | StellariumInferencePaymentManifest;
|
|
18
|
+
/** Fixed functions return one value. Inference functions run AI inference and are priced per token. */
|
|
19
|
+
export type StellariumResourceFunctionKind = 'fixed' | 'inference';
|
|
9
20
|
export interface StellariumResourceFunctionParameter extends ResourceFunctionParameter {
|
|
10
21
|
description?: string;
|
|
11
22
|
}
|
|
@@ -16,6 +27,13 @@ export interface StellariumResourceFunction extends ResourceFunction {
|
|
|
16
27
|
paymentManifest?: StellariumResourceFunctionPaymentManifest;
|
|
17
28
|
/** Callers need a grant from the hosting node. Announced to peers so callers can tell. */
|
|
18
29
|
restricted?: boolean;
|
|
30
|
+
/** Absent means 'fixed'. An inference function takes a `maxTokens: int32` parameter and its `exec` returns `{ value, usage }`. */
|
|
31
|
+
kind?: StellariumResourceFunctionKind;
|
|
32
|
+
/**
|
|
33
|
+
* Input tokens a call will use, for pricing its voucher before inference runs. Required on paid inference
|
|
34
|
+
* functions. It runs only on the hosting node and, like `exec`, is never announced.
|
|
35
|
+
*/
|
|
36
|
+
estimateInputTokens?: (args: ResourceFunctionCallParameter[]) => Promise<number>;
|
|
19
37
|
}
|
|
20
38
|
export interface StellariumResource extends Resource {
|
|
21
39
|
id: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/resource/repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/resource/repository.ts"],"names":[],"mappings":"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"}
|