stellarium-ts 0.16.0 → 0.18.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/operatorAuth.d.ts +19 -0
- package/dist/client/operatorAuth.d.ts.map +1 -0
- package/dist/client/operatorAuth.js +21 -0
- package/dist/client/operatorClient.d.ts +103 -31
- package/dist/client/operatorClient.d.ts.map +1 -1
- package/dist/client/operatorClient.js +107 -23
- package/dist/client/runTokens.d.ts +23 -0
- package/dist/client/runTokens.d.ts.map +1 -0
- package/dist/client/runTokens.js +34 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +8 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/resource/inference.d.ts +5 -3
- package/dist/resource/inference.d.ts.map +1 -1
- package/dist/resource/inference.js +17 -7
- package/dist/resource/invoker.d.ts +48 -14
- package/dist/resource/invoker.d.ts.map +1 -1
- package/dist/resource/invoker.js +108 -38
- package/dist/resource/repository.d.ts +13 -7
- package/dist/resource/repository.d.ts.map +1 -1
- package/dist/resource/repository.js +23 -13
- package/dist/resource/settlement.d.ts +25 -0
- package/dist/resource/settlement.d.ts.map +1 -0
- package/dist/resource/settlement.js +59 -0
- package/dist/routes.d.ts +16 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +103 -76
- package/dist/server.d.ts +11 -6
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +32 -7
- package/dist/service.d.ts +18 -6
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +8 -3
- package/dist/tests/client/operatorClient.test.js +94 -36
- package/dist/tests/db/db.test.js +4 -4
- package/dist/tests/helpers.d.ts +4 -3
- package/dist/tests/helpers.d.ts.map +1 -1
- package/dist/tests/helpers.js +8 -6
- package/dist/tests/integration/network.test.js +69 -18
- package/dist/tests/resource/invoker.test.js +33 -9
- package/dist/tests/resource/repository.test.js +9 -9
- package/dist/tests/routes.test.js +165 -40
- package/dist/tests/server.test.js +15 -15
- package/package.json +3 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Resource, ResourceFunctionCallParameter, ResourceInvoker } from "requestscript";
|
|
2
|
+
import type { OperatorClient } from "../client/operatorClient.js";
|
|
2
3
|
import type { GrantService } from "../grant/service.js";
|
|
3
4
|
import { type InferenceUsage } from "./inference.js";
|
|
4
5
|
import { ResourceRepository, type StellariumInferencePaymentManifest } from "./repository.js";
|
|
@@ -6,11 +7,13 @@ export interface PaymentVoucher {
|
|
|
6
7
|
requestId: string;
|
|
7
8
|
invocationId: string;
|
|
8
9
|
amount: number;
|
|
9
|
-
|
|
10
|
+
recipientOrganizationId: string;
|
|
10
11
|
signature: string;
|
|
11
12
|
expiresAt: string;
|
|
12
13
|
invocationHash: string;
|
|
13
14
|
publicKey: string;
|
|
15
|
+
/** Set for a call made by a script run billed to an API client: the run's hold pays instead of this node's organization. */
|
|
16
|
+
runId?: string;
|
|
14
17
|
}
|
|
15
18
|
/**
|
|
16
19
|
* A voucher for an inference function. It signs the provider's per-million-token rates and the call's
|
|
@@ -22,14 +25,16 @@ export interface InferencePaymentVoucher {
|
|
|
22
25
|
requestId: string;
|
|
23
26
|
invocationId: string;
|
|
24
27
|
maxAmount: number;
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
inputCostMicroUsdPerMillionTokens: number;
|
|
29
|
+
outputCostMicroUsdPerMillionTokens: number;
|
|
27
30
|
maxTokens: number;
|
|
28
|
-
|
|
31
|
+
recipientOrganizationId: string;
|
|
29
32
|
signature: string;
|
|
30
33
|
expiresAt: string;
|
|
31
34
|
invocationHash: string;
|
|
32
35
|
publicKey: string;
|
|
36
|
+
/** Set for a call made by a script run billed to an API client: the run's hold pays instead of this node's organization. */
|
|
37
|
+
runId?: string;
|
|
33
38
|
}
|
|
34
39
|
/** What a provider's `POST /v1/run/resource/estimate` answers. */
|
|
35
40
|
export interface InferenceEstimate {
|
|
@@ -50,24 +55,26 @@ export declare function paymentVoucherPayload(voucher: Omit<PaymentVoucher, "sig
|
|
|
50
55
|
requestId: string;
|
|
51
56
|
invocationId: string;
|
|
52
57
|
amount: number;
|
|
53
|
-
|
|
58
|
+
recipientOrganizationId: string;
|
|
54
59
|
expiresAt: string;
|
|
55
60
|
invocationHash: string;
|
|
56
61
|
publicKey: string;
|
|
62
|
+
runId?: string;
|
|
57
63
|
};
|
|
58
64
|
/**
|
|
59
65
|
* The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
|
|
60
66
|
* Key order is part of the signed bytes.
|
|
61
67
|
*/
|
|
62
68
|
export declare function inferenceVoucherPayload(voucher: Omit<InferencePaymentVoucher, "signature">): {
|
|
69
|
+
runId?: string;
|
|
63
70
|
kind: "inference";
|
|
64
71
|
requestId: string;
|
|
65
72
|
invocationId: string;
|
|
66
73
|
maxAmount: number;
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
inputCostMicroUsdPerMillionTokens: number;
|
|
75
|
+
outputCostMicroUsdPerMillionTokens: number;
|
|
69
76
|
maxTokens: number;
|
|
70
|
-
|
|
77
|
+
recipientOrganizationId: string;
|
|
71
78
|
expiresAt: string;
|
|
72
79
|
invocationHash: string;
|
|
73
80
|
publicKey: string;
|
|
@@ -82,18 +89,36 @@ export declare const EXTERNAL_INVOKE_BASE_DELAY_MS = 500;
|
|
|
82
89
|
export declare const EXTERNAL_INVOKE_TIMEOUT_MS = 30000;
|
|
83
90
|
/** Per attempt, for calls to inference functions. Every attempt fits within `INFERENCE_VOUCHER_LIFETIME_MS`. */
|
|
84
91
|
export declare const INFERENCE_INVOKE_TIMEOUT_MS = 120000;
|
|
92
|
+
/** A script run billed to another organization's API client. Its paid calls draw on the run's hold. */
|
|
93
|
+
export interface RunContext {
|
|
94
|
+
runId: string;
|
|
95
|
+
}
|
|
96
|
+
/** How a node settles its own paid functions when a billed run calls them. */
|
|
97
|
+
export interface LocalSettlement {
|
|
98
|
+
operatorClient: OperatorClient;
|
|
99
|
+
/** The organization this node's functions pay. */
|
|
100
|
+
organizationId?: string;
|
|
101
|
+
}
|
|
85
102
|
export declare class CombinationResourceInvoker implements ResourceInvoker {
|
|
86
103
|
private readonly resourceRepository;
|
|
87
|
-
private readonly
|
|
104
|
+
private readonly costLimitMicroUsd;
|
|
88
105
|
private readonly publicKey;
|
|
89
106
|
private readonly privateKey;
|
|
90
107
|
private readonly grantService?;
|
|
91
|
-
private readonly
|
|
108
|
+
private readonly inferenceCostLimitMicroUsd;
|
|
109
|
+
private readonly localSettlement?;
|
|
92
110
|
/**
|
|
93
111
|
* Without a `grantService`, calls to restricted remote functions fail.
|
|
94
|
-
* `
|
|
112
|
+
* `inferenceCostLimitMicroUsd` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsd`.
|
|
113
|
+
* Without `localSettlement`, a billed run can't call this node's own paid functions.
|
|
114
|
+
*/
|
|
115
|
+
constructor(resourceRepository: ResourceRepository, costLimitMicroUsd: number, publicKey: string, privateKey: string, grantService?: GrantService | undefined, inferenceCostLimitMicroUsd?: number, localSettlement?: LocalSettlement | undefined);
|
|
116
|
+
/**
|
|
117
|
+
* An invoker for one billed run. Its paid calls, remote or to this node's own paid functions, carry
|
|
118
|
+
* the run's id, so the run's hold pays for them. The run's hold limits what they cost, in place of the
|
|
119
|
+
* cost limits.
|
|
95
120
|
*/
|
|
96
|
-
|
|
121
|
+
forRun(run: RunContext): ResourceInvoker;
|
|
97
122
|
/**
|
|
98
123
|
* Invoke a function on a resource.
|
|
99
124
|
* If the resource has a baseUrl, it will be invoked as a remote function.
|
|
@@ -114,9 +139,18 @@ export declare class CombinationResourceInvoker implements ResourceInvoker {
|
|
|
114
139
|
} | null;
|
|
115
140
|
usage?: InferenceUsage;
|
|
116
141
|
}>;
|
|
142
|
+
private invokeIn;
|
|
143
|
+
private execLocal;
|
|
144
|
+
/**
|
|
145
|
+
* Calls one of this node's paid functions for a billed run: signs a voucher with the run's id that pays
|
|
146
|
+
* this node's organization, then authorizes, runs and settles it as `POST /v1/run/resource` would.
|
|
147
|
+
*/
|
|
148
|
+
private invokeLocalPaid;
|
|
117
149
|
/** Asks the provider what a call to a paid inference function will cost at most. */
|
|
118
150
|
private estimate;
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
/** With `runId`, the voucher is paid from that run's hold. */
|
|
152
|
+
createPaymentVoucher(recipientOrganizationId: string, amount: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[], runId?: string): PaymentVoucher;
|
|
153
|
+
/** With `runId`, the voucher is paid from that run's hold. */
|
|
154
|
+
createInferenceVoucher(manifest: StellariumInferencePaymentManifest, maxAmount: number, maxTokens: number, resourcePath: string, resourceName: string, functionName: string, parameters: ResourceFunctionCallParameter[], runId?: string): InferencePaymentVoucher;
|
|
121
155
|
}
|
|
122
156
|
//# 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;
|
|
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,5 +1,6 @@
|
|
|
1
1
|
import { hashData, signData } from "../crypto/utils.js";
|
|
2
|
-
import { inferenceCharge, isInferenceFunction, isInferenceManifest, parseInferenceResult, parseMaxTokens } from "./inference.js";
|
|
2
|
+
import { estimateInputTokens, inferenceCharge, isInferenceFunction, isInferenceManifest, parseInferenceResult, parseMaxTokens } from "./inference.js";
|
|
3
|
+
import { settlePaidCall } from "./settlement.js";
|
|
3
4
|
/**
|
|
4
5
|
* Hash binding a voucher to one function call. Providers recompute it from the
|
|
5
6
|
* request they receive and compare it to `voucher.invocationHash`.
|
|
@@ -13,16 +14,20 @@ export function invocationHash(resourcePath, resourceName, functionName, paramet
|
|
|
13
14
|
* Key order is part of the signed bytes.
|
|
14
15
|
*/
|
|
15
16
|
export function paymentVoucherPayload(voucher) {
|
|
16
|
-
const { requestId, invocationId, amount,
|
|
17
|
-
return { requestId, invocationId, amount,
|
|
17
|
+
const { requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash, publicKey } = voucher;
|
|
18
|
+
return { requestId, invocationId, amount, recipientOrganizationId, expiresAt, invocationHash, publicKey, ...runPayload(voucher.runId) };
|
|
19
|
+
}
|
|
20
|
+
/** `runId` is signed last, and only when set, so a voucher without one signs the same bytes as before it existed. */
|
|
21
|
+
function runPayload(runId) {
|
|
22
|
+
return runId === undefined ? {} : { runId };
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
20
25
|
* The object `createInferenceVoucher` signs: every inference voucher field except `signature`.
|
|
21
26
|
* Key order is part of the signed bytes.
|
|
22
27
|
*/
|
|
23
28
|
export function inferenceVoucherPayload(voucher) {
|
|
24
|
-
const { kind, requestId, invocationId, maxAmount,
|
|
25
|
-
return { kind, requestId, invocationId, maxAmount,
|
|
29
|
+
const { kind, requestId, invocationId, maxAmount, inputCostMicroUsdPerMillionTokens, outputCostMicroUsdPerMillionTokens, maxTokens, recipientOrganizationId, expiresAt, invocationHash, publicKey } = voucher;
|
|
30
|
+
return { kind, requestId, invocationId, maxAmount, inputCostMicroUsdPerMillionTokens, outputCostMicroUsdPerMillionTokens, maxTokens, recipientOrganizationId, expiresAt, invocationHash, publicKey, ...runPayload(voucher.runId) };
|
|
26
31
|
}
|
|
27
32
|
export function isInferenceVoucher(voucher) {
|
|
28
33
|
return voucher.kind === 'inference';
|
|
@@ -63,22 +68,33 @@ async function fetchWithRetry(url, init, timeoutMs = EXTERNAL_INVOKE_TIMEOUT_MS)
|
|
|
63
68
|
}
|
|
64
69
|
export class CombinationResourceInvoker {
|
|
65
70
|
resourceRepository;
|
|
66
|
-
|
|
71
|
+
costLimitMicroUsd;
|
|
67
72
|
publicKey;
|
|
68
73
|
privateKey;
|
|
69
74
|
grantService;
|
|
70
|
-
|
|
75
|
+
inferenceCostLimitMicroUsd;
|
|
76
|
+
localSettlement;
|
|
71
77
|
/**
|
|
72
78
|
* Without a `grantService`, calls to restricted remote functions fail.
|
|
73
|
-
* `
|
|
79
|
+
* `inferenceCostLimitMicroUsd` caps an inference voucher's `maxAmount`, and defaults to `costLimitMicroUsd`.
|
|
80
|
+
* Without `localSettlement`, a billed run can't call this node's own paid functions.
|
|
74
81
|
*/
|
|
75
|
-
constructor(resourceRepository,
|
|
82
|
+
constructor(resourceRepository, costLimitMicroUsd, publicKey, privateKey, grantService, inferenceCostLimitMicroUsd = costLimitMicroUsd, localSettlement) {
|
|
76
83
|
this.resourceRepository = resourceRepository;
|
|
77
|
-
this.
|
|
84
|
+
this.costLimitMicroUsd = costLimitMicroUsd;
|
|
78
85
|
this.publicKey = publicKey;
|
|
79
86
|
this.privateKey = privateKey;
|
|
80
87
|
this.grantService = grantService;
|
|
81
|
-
this.
|
|
88
|
+
this.inferenceCostLimitMicroUsd = inferenceCostLimitMicroUsd;
|
|
89
|
+
this.localSettlement = localSettlement;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* An invoker for one billed run. Its paid calls, remote or to this node's own paid functions, carry
|
|
93
|
+
* the run's id, so the run's hold pays for them. The run's hold limits what they cost, in place of the
|
|
94
|
+
* cost limits.
|
|
95
|
+
*/
|
|
96
|
+
forRun(run) {
|
|
97
|
+
return { invoke: (resource, functionName, parameters) => this.invokeIn(resource, functionName, parameters, run) };
|
|
82
98
|
}
|
|
83
99
|
/**
|
|
84
100
|
* Invoke a function on a resource.
|
|
@@ -94,6 +110,9 @@ export class CombinationResourceInvoker {
|
|
|
94
110
|
* @returns The return value of the function
|
|
95
111
|
*/
|
|
96
112
|
async invoke(resource, functionName, parameters) {
|
|
113
|
+
return this.invokeIn(resource, functionName, parameters, undefined);
|
|
114
|
+
}
|
|
115
|
+
async invokeIn(resource, functionName, parameters, run) {
|
|
97
116
|
if (resource.metadata.baseUrl) {
|
|
98
117
|
let requestBody = {
|
|
99
118
|
resourcePath: resource.path,
|
|
@@ -117,11 +136,14 @@ export class CombinationResourceInvoker {
|
|
|
117
136
|
}
|
|
118
137
|
const manifest = resourceFunction.paymentManifest;
|
|
119
138
|
const inference = isInferenceFunction(resourceFunction);
|
|
139
|
+
if (manifest && !manifest.recipientOrganizationId) {
|
|
140
|
+
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: its payment manifest names no recipient organization` } };
|
|
141
|
+
}
|
|
120
142
|
if (manifest && inference) {
|
|
121
143
|
console.log(`Resource function ${resource.name}.${functionName} is a paid inference function, creating payment voucher...`);
|
|
122
144
|
// Peers' manifests aren't validated on receipt.
|
|
123
|
-
if (!isInferenceManifest(manifest) || !Number.isSafeInteger(manifest.
|
|
124
|
-
|| !Number.isSafeInteger(manifest.
|
|
145
|
+
if (!isInferenceManifest(manifest) || !Number.isSafeInteger(manifest.inputCostMicroUsdPerMillionTokens) || manifest.inputCostMicroUsdPerMillionTokens < 0
|
|
146
|
+
|| !Number.isSafeInteger(manifest.outputCostMicroUsdPerMillionTokens) || manifest.outputCostMicroUsdPerMillionTokens < 0) {
|
|
125
147
|
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: its payment manifest has no valid per-token pricing` } };
|
|
126
148
|
}
|
|
127
149
|
const maxTokens = parseMaxTokens(parameters);
|
|
@@ -136,20 +158,20 @@ export class CombinationResourceInvoker {
|
|
|
136
158
|
if (inferenceCharge(manifest, estimate.inputTokens, maxTokens) !== estimate.maxAmount) {
|
|
137
159
|
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: the provider's estimate doesn't match its advertised rates` } };
|
|
138
160
|
}
|
|
139
|
-
if (estimate.maxAmount > this.
|
|
140
|
-
throw new Error(`Function ${functionName} may cost up to ${estimate.maxAmount} micro-
|
|
161
|
+
if (!run && estimate.maxAmount > this.inferenceCostLimitMicroUsd) {
|
|
162
|
+
throw new Error(`Function ${functionName} may cost up to ${estimate.maxAmount} micro-USD, which exceeds the inference cost limit of ${this.inferenceCostLimitMicroUsd} micro-USD`);
|
|
141
163
|
}
|
|
142
|
-
requestBody.paymentVoucher = this.createInferenceVoucher(manifest, estimate.maxAmount, maxTokens, resource.path, resource.name, functionName, parameters);
|
|
164
|
+
requestBody.paymentVoucher = this.createInferenceVoucher(manifest, estimate.maxAmount, maxTokens, resource.path, resource.name, functionName, parameters, run?.runId);
|
|
143
165
|
}
|
|
144
166
|
else if (manifest) {
|
|
145
167
|
console.log(`Resource function ${resource.name}.${functionName} has a payment manifest, creating payment voucher...`);
|
|
146
168
|
if (isInferenceManifest(manifest)) {
|
|
147
169
|
return { value: null, error: { message: `Failed to invoke ${resource.name}.${functionName}: it has per-token pricing but isn't an inference function` } };
|
|
148
170
|
}
|
|
149
|
-
if (manifest.
|
|
150
|
-
throw new Error(`Function ${functionName} cost ${manifest.
|
|
171
|
+
if (!run && manifest.costMicroUsd > this.costLimitMicroUsd) {
|
|
172
|
+
throw new Error(`Function ${functionName} cost ${manifest.costMicroUsd} micro-USD, which exceeds the cost limit of ${this.costLimitMicroUsd} micro-USD`);
|
|
151
173
|
}
|
|
152
|
-
requestBody.paymentVoucher = this.createPaymentVoucher(manifest.
|
|
174
|
+
requestBody.paymentVoucher = this.createPaymentVoucher(manifest.recipientOrganizationId, manifest.costMicroUsd, resource.path, resource.name, functionName, parameters, run?.runId);
|
|
153
175
|
}
|
|
154
176
|
// The voucher is created once and reused on every attempt, so the operator's
|
|
155
177
|
// requestId/invocationId check prevents a retried call from being settled twice.
|
|
@@ -175,16 +197,56 @@ export class CombinationResourceInvoker {
|
|
|
175
197
|
if (!fn) {
|
|
176
198
|
return { value: null, error: { message: `Function ${functionName} not found in resource ${resource.name}` } };
|
|
177
199
|
}
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
// A billed run pays for this node's own paid functions as a remote caller would.
|
|
201
|
+
if (run && fn.paymentManifest) {
|
|
202
|
+
return this.invokeLocalPaid(resource, fn, parameters, run);
|
|
203
|
+
}
|
|
204
|
+
return this.execLocal(fn, parameters);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async execLocal(fn, parameters) {
|
|
208
|
+
const result = await fn.exec(parameters);
|
|
209
|
+
if (isInferenceFunction(fn)) {
|
|
210
|
+
const inferenceResult = parseInferenceResult(result);
|
|
211
|
+
if (!inferenceResult) {
|
|
212
|
+
return { value: null, error: { message: `Inference function ${fn.name} must return { value, usage: { inputTokens, outputTokens } }` } };
|
|
213
|
+
}
|
|
214
|
+
return { value: inferenceResult.value, error: null, usage: inferenceResult.usage };
|
|
215
|
+
}
|
|
216
|
+
return { value: result, error: null };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Calls one of this node's paid functions for a billed run: signs a voucher with the run's id that pays
|
|
220
|
+
* this node's organization, then authorizes, runs and settles it as `POST /v1/run/resource` would.
|
|
221
|
+
*/
|
|
222
|
+
async invokeLocalPaid(resource, fn, parameters, run) {
|
|
223
|
+
const failed = (reason) => ({ value: null, error: { message: `Failed to invoke ${resource.name}.${fn.name}: ${reason}` } });
|
|
224
|
+
const organizationId = this.localSettlement?.organizationId;
|
|
225
|
+
if (!this.localSettlement || !organizationId) {
|
|
226
|
+
return failed('this node is not enrolled in an organization that can be paid');
|
|
227
|
+
}
|
|
228
|
+
const manifest = fn.paymentManifest;
|
|
229
|
+
let voucher;
|
|
230
|
+
if (isInferenceFunction(fn) && isInferenceManifest(manifest)) {
|
|
231
|
+
const maxTokens = parseMaxTokens(parameters);
|
|
232
|
+
if (maxTokens === undefined) {
|
|
233
|
+
return failed('maxTokens must be a positive int32');
|
|
234
|
+
}
|
|
235
|
+
const inputTokens = await estimateInputTokens(fn, parameters);
|
|
236
|
+
if (inputTokens === undefined) {
|
|
237
|
+
return failed('failed to estimate input tokens');
|
|
185
238
|
}
|
|
186
|
-
|
|
239
|
+
const maxAmount = inferenceCharge(manifest, inputTokens, maxTokens);
|
|
240
|
+
voucher = this.createInferenceVoucher({ ...manifest, recipientOrganizationId: organizationId }, maxAmount, maxTokens, resource.path, resource.name, fn.name, parameters, run.runId);
|
|
241
|
+
}
|
|
242
|
+
else if (!isInferenceManifest(manifest)) {
|
|
243
|
+
voucher = this.createPaymentVoucher(organizationId, manifest.costMicroUsd, resource.path, resource.name, fn.name, parameters, run.runId);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return failed('it has per-token pricing but isn\'t an inference function');
|
|
187
247
|
}
|
|
248
|
+
const result = await settlePaidCall(this.localSettlement.operatorClient, voucher, () => this.execLocal(fn, parameters), (error, message) => console.error(message, error));
|
|
249
|
+
return 'refused' in result ? failed(result.refused.error) : result;
|
|
188
250
|
}
|
|
189
251
|
/** Asks the provider what a call to a paid inference function will cost at most. */
|
|
190
252
|
async estimate(url, target, chain) {
|
|
@@ -210,27 +272,35 @@ export class CombinationResourceInvoker {
|
|
|
210
272
|
}
|
|
211
273
|
return { inputTokens: inputTokens, maxAmount: maxAmount };
|
|
212
274
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
275
|
+
/** With `runId`, the voucher is paid from that run's hold. */
|
|
276
|
+
createPaymentVoucher(recipientOrganizationId, amount, resourcePath, resourceName, functionName, parameters, runId) {
|
|
277
|
+
const unsigned = {
|
|
278
|
+
requestId: crypto.randomUUID(),
|
|
279
|
+
invocationId: crypto.randomUUID(),
|
|
280
|
+
amount,
|
|
281
|
+
recipientOrganizationId,
|
|
282
|
+
expiresAt: new Date(Date.now() + PAYMENT_VOUCHER_LIFETIME_MS).toISOString(),
|
|
283
|
+
invocationHash: invocationHash(resourcePath, resourceName, functionName, parameters),
|
|
284
|
+
publicKey: this.publicKey,
|
|
285
|
+
...runPayload(runId),
|
|
286
|
+
};
|
|
287
|
+
return { ...unsigned, signature: signData(paymentVoucherPayload(unsigned), this.privateKey) };
|
|
220
288
|
}
|
|
221
|
-
|
|
289
|
+
/** With `runId`, the voucher is paid from that run's hold. */
|
|
290
|
+
createInferenceVoucher(manifest, maxAmount, maxTokens, resourcePath, resourceName, functionName, parameters, runId) {
|
|
222
291
|
const unsigned = {
|
|
223
292
|
kind: 'inference',
|
|
224
293
|
requestId: crypto.randomUUID(),
|
|
225
294
|
invocationId: crypto.randomUUID(),
|
|
226
295
|
maxAmount,
|
|
227
|
-
|
|
228
|
-
|
|
296
|
+
inputCostMicroUsdPerMillionTokens: manifest.inputCostMicroUsdPerMillionTokens,
|
|
297
|
+
outputCostMicroUsdPerMillionTokens: manifest.outputCostMicroUsdPerMillionTokens,
|
|
229
298
|
maxTokens,
|
|
230
|
-
|
|
299
|
+
recipientOrganizationId: manifest.recipientOrganizationId,
|
|
231
300
|
expiresAt: new Date(Date.now() + INFERENCE_VOUCHER_LIFETIME_MS).toISOString(),
|
|
232
301
|
invocationHash: invocationHash(resourcePath, resourceName, functionName, parameters),
|
|
233
302
|
publicKey: this.publicKey,
|
|
303
|
+
...runPayload(runId),
|
|
234
304
|
};
|
|
235
305
|
return { ...unsigned, signature: signData(inferenceVoucherPayload(unsigned), this.privateKey) };
|
|
236
306
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
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
|
+
/**
|
|
5
|
+
* A flat price per call. `recipientOrganizationId` is filled in by the hosting node when it advertises
|
|
6
|
+
* the function, from the organization it is enrolled in; resource authors leave it out.
|
|
7
|
+
*/
|
|
5
8
|
export interface StellariumFixedPaymentManifest {
|
|
6
9
|
id?: number;
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
costMicroUsd: number;
|
|
11
|
+
recipientOrganizationId?: string;
|
|
9
12
|
}
|
|
10
13
|
/** Per-token pricing for an inference function. Either rate may be 0. */
|
|
11
14
|
export interface StellariumInferencePaymentManifest {
|
|
12
15
|
id?: number;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
inputCostMicroUsdPerMillionTokens: number;
|
|
17
|
+
outputCostMicroUsdPerMillionTokens: number;
|
|
18
|
+
recipientOrganizationId?: string;
|
|
16
19
|
}
|
|
17
20
|
export type StellariumResourceFunctionPaymentManifest = StellariumFixedPaymentManifest | StellariumInferencePaymentManifest;
|
|
18
21
|
/** Fixed functions return one value. Inference functions run AI inference and are priced per token. */
|
|
@@ -61,8 +64,10 @@ export declare class ResourceRepository {
|
|
|
61
64
|
private readonly db;
|
|
62
65
|
private resources;
|
|
63
66
|
private readonly baseUrl;
|
|
67
|
+
private readonly organizationId?;
|
|
64
68
|
private writes;
|
|
65
|
-
|
|
69
|
+
/** `organizationId` is the operator organization this node is enrolled in, which is paid for its functions. */
|
|
70
|
+
constructor(db: Database, resources?: StellariumResource[], baseUrl?: string, organizationId?: string | undefined);
|
|
66
71
|
private serialize;
|
|
67
72
|
private transaction;
|
|
68
73
|
private isInternal;
|
|
@@ -84,6 +89,7 @@ export declare class ResourceRepository {
|
|
|
84
89
|
private deleteFunctions;
|
|
85
90
|
getInternalResource(path: string, name: string): Promise<StellariumResource | undefined>;
|
|
86
91
|
getResource(key: string): Promise<Resource | undefined>;
|
|
92
|
+
/** This node's resources, with its base URL and its organization as the recipient of every paid function. */
|
|
87
93
|
getInternalResources(): Promise<StellariumResource[]>;
|
|
88
94
|
/** Learned resources in insertion order. A negative `limit` means no limit. */
|
|
89
95
|
getExternalResources(offset?: number, limit?: number): Promise<StellariumResource[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/resource/repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACrH,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/resource/repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACrH,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC3C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,yEAAyE;AACzE,MAAM,WAAW,kCAAkC;IAC/C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iCAAiC,EAAE,MAAM,CAAC;IAC1C,kCAAkC,EAAE,MAAM,CAAC;IAC3C,uBAAuB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,MAAM,yCAAyC,GAAG,8BAA8B,GAAG,kCAAkC,CAAC;AAE5H,uGAAuG;AACvG,MAAM,MAAM,8BAA8B,GAAG,OAAO,GAAG,WAAW,CAAC;AAEnE,MAAM,WAAW,mCAAoC,SAAQ,yBAAyB;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,mCAAmC,EAAE,CAAC;IAClD,eAAe,CAAC,EAAE,yCAAyC,CAAC;IAC5D,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kIAAkI;IAClI,IAAI,CAAC,EAAE,8BAA8B,CAAC;IACtC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,6BAA6B,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,0BAA0B,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,MAAM,EAAE,MAAM;;;EAE1F;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC;AA4B7E,qBAAa,kBAAkB;IAKf,OAAO,CAAC,QAAQ,CAAC,EAAE;IAAY,OAAO,CAAC,SAAS;IAA6B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAe,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAHhK,OAAO,CAAC,MAAM,CAAuC;IAErD,+GAA+G;IAC/G,YAA6B,EAAE,EAAE,QAAQ,EAAU,SAAS,GAAE,kBAAkB,EAAO,EAAmB,OAAO,GAAE,MAAW,EAAmB,cAAc,CAAC,EAAE,MAAM,YAAA,EAAI;IAE5K,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,UAAU;IAIlB;;;OAGG;IACG,KAAK,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUvD;IAED;;;;;;OAMG;IACG,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAoC1H;YAEa,MAAM;YAcN,eAAe;YAoCf,eAAe;IAOvB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAE7F;IAEK,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CA6B5D;IAED,6GAA6G;IACvG,oBAAoB,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQ1D;IAED,+EAA+E;IACzE,oBAAoB,CAAC,MAAM,SAAI,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAgBhF;IAEK,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAsB3I;IAEK,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,EAAE,CAAC,CAapF;IAEK,qBAAqB,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,mCAAmC,EAAE,CAAC,CAMtG;IAEK,kCAAkC,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,yCAAyC,GAAG,SAAS,CAAC,CAkBnI;IAED;;;OAGG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQ/E;CACJ"}
|
|
@@ -24,12 +24,15 @@ export class ResourceRepository {
|
|
|
24
24
|
db;
|
|
25
25
|
resources;
|
|
26
26
|
baseUrl;
|
|
27
|
+
organizationId;
|
|
27
28
|
// The connection is shared, so transactions are serialized to keep them from nesting.
|
|
28
29
|
writes = Promise.resolve();
|
|
29
|
-
|
|
30
|
+
/** `organizationId` is the operator organization this node is enrolled in, which is paid for its functions. */
|
|
31
|
+
constructor(db, resources = [], baseUrl = '', organizationId) {
|
|
30
32
|
this.db = db;
|
|
31
33
|
this.resources = resources;
|
|
32
34
|
this.baseUrl = baseUrl;
|
|
35
|
+
this.organizationId = organizationId;
|
|
33
36
|
}
|
|
34
37
|
serialize(work) {
|
|
35
38
|
const run = this.writes.then(work, work);
|
|
@@ -123,12 +126,12 @@ export class ResourceRepository {
|
|
|
123
126
|
const manifest = fn.paymentManifest;
|
|
124
127
|
if (manifest) {
|
|
125
128
|
const inference = isInferenceManifest(manifest);
|
|
126
|
-
await this.db.run('INSERT INTO resource_function_payment_manifest (resource_function_id,
|
|
129
|
+
await this.db.run('INSERT INTO resource_function_payment_manifest (resource_function_id, cost_micro_usd, input_cost_micro_usd_per_million_tokens, output_cost_micro_usd_per_million_tokens, recipient_organization_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
127
130
|
functionId,
|
|
128
|
-
inference ? null : manifest.
|
|
129
|
-
inference ? manifest.
|
|
130
|
-
inference ? manifest.
|
|
131
|
-
manifest.
|
|
131
|
+
inference ? null : manifest.costMicroUsd,
|
|
132
|
+
inference ? manifest.inputCostMicroUsdPerMillionTokens : null,
|
|
133
|
+
inference ? manifest.outputCostMicroUsdPerMillionTokens : null,
|
|
134
|
+
manifest.recipientOrganizationId ?? null,
|
|
132
135
|
createdAt,
|
|
133
136
|
createdAt,
|
|
134
137
|
]);
|
|
@@ -167,8 +170,15 @@ export class ResourceRepository {
|
|
|
167
170
|
}
|
|
168
171
|
};
|
|
169
172
|
}
|
|
173
|
+
/** This node's resources, with its base URL and its organization as the recipient of every paid function. */
|
|
170
174
|
async getInternalResources() {
|
|
171
|
-
return this.resources.map(r => ({
|
|
175
|
+
return this.resources.map(r => ({
|
|
176
|
+
...r,
|
|
177
|
+
baseUrl: this.baseUrl,
|
|
178
|
+
functions: r.functions.map((fn) => fn.paymentManifest
|
|
179
|
+
? { ...fn, paymentManifest: { ...fn.paymentManifest, recipientOrganizationId: this.organizationId } }
|
|
180
|
+
: fn),
|
|
181
|
+
}));
|
|
172
182
|
}
|
|
173
183
|
/** Learned resources in insertion order. A negative `limit` means no limit. */
|
|
174
184
|
async getExternalResources(offset = 0, limit = -1) {
|
|
@@ -231,18 +241,18 @@ export class ResourceRepository {
|
|
|
231
241
|
if (!result) {
|
|
232
242
|
return undefined;
|
|
233
243
|
}
|
|
234
|
-
if (result.
|
|
244
|
+
if (result.cost_micro_usd === null) {
|
|
235
245
|
return {
|
|
236
246
|
id: result.id,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
inputCostMicroUsdPerMillionTokens: result.input_cost_micro_usd_per_million_tokens,
|
|
248
|
+
outputCostMicroUsdPerMillionTokens: result.output_cost_micro_usd_per_million_tokens,
|
|
249
|
+
recipientOrganizationId: result.recipient_organization_id ?? undefined,
|
|
240
250
|
};
|
|
241
251
|
}
|
|
242
252
|
return {
|
|
243
253
|
id: result.id,
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
costMicroUsd: result.cost_micro_usd,
|
|
255
|
+
recipientOrganizationId: result.recipient_organization_id ?? undefined,
|
|
246
256
|
};
|
|
247
257
|
}
|
|
248
258
|
/**
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { OperatorClient } from "../client/operatorClient.js";
|
|
2
|
+
import { type InferenceUsage } from "./inference.js";
|
|
3
|
+
import { type InferencePaymentVoucher, type PaymentVoucher } from "./invoker.js";
|
|
4
|
+
/** What a resource function call evaluates to, with an inference function's usage. */
|
|
5
|
+
export interface InvokeResult {
|
|
6
|
+
value: unknown;
|
|
7
|
+
error: {
|
|
8
|
+
message: string;
|
|
9
|
+
} | null;
|
|
10
|
+
usage?: InferenceUsage;
|
|
11
|
+
}
|
|
12
|
+
/** The operator refused to hold the voucher, so the call didn't run. */
|
|
13
|
+
export interface PaymentRefusal {
|
|
14
|
+
refused: {
|
|
15
|
+
status: number;
|
|
16
|
+
error: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Runs a paid call: holds the voucher's amount with the operator first, so a paid function never runs
|
|
21
|
+
* unpaid, then runs `call` and captures the voucher, or releases it for a failed or free call. Settlement is
|
|
22
|
+
* scheduled after returning, so it doesn't delay the caller, and failures go to `onSettleError`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function settlePaidCall(operatorClient: OperatorClient, voucher: PaymentVoucher | InferencePaymentVoucher, call: () => Promise<InvokeResult>, onSettleError: (error: unknown, message: string) => void): Promise<InvokeResult | PaymentRefusal>;
|
|
25
|
+
//# sourceMappingURL=settlement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settlement.d.ts","sourceRoot":"","sources":["../../src/resource/settlement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,6BAA6B,CAAC;AACrF,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,KAAK,uBAAuB,EAAsB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAErG,sFAAsF;AACtF,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C;AAYD;;;;GAIG;AACH,wBAAsB,cAAc,CAChC,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,cAAc,GAAG,uBAAuB,EACjD,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,EACjC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACzD,OAAO,CAAC,YAAY,GAAG,cAAc,CAAC,CAuCxC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { inferenceCharge } from "./inference.js";
|
|
2
|
+
import { isInferenceVoucher } from "./invoker.js";
|
|
3
|
+
/** What the operator will capture for `usage`: its charge at the voucher's rates, capped at `maxAmount`. */
|
|
4
|
+
function cappedInferenceCharge(voucher, usage) {
|
|
5
|
+
try {
|
|
6
|
+
return Math.min(inferenceCharge(voucher, usage.inputTokens, usage.outputTokens), voucher.maxAmount);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
// The charge isn't a safe integer, so it is over any maxAmount.
|
|
10
|
+
return voucher.maxAmount;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Runs a paid call: holds the voucher's amount with the operator first, so a paid function never runs
|
|
15
|
+
* unpaid, then runs `call` and captures the voucher, or releases it for a failed or free call. Settlement is
|
|
16
|
+
* scheduled after returning, so it doesn't delay the caller, and failures go to `onSettleError`.
|
|
17
|
+
*/
|
|
18
|
+
export async function settlePaidCall(operatorClient, voucher, call, onSettleError) {
|
|
19
|
+
const authorization = await operatorClient.authorize(voucher);
|
|
20
|
+
if (!authorization.ok) {
|
|
21
|
+
return { refused: { status: authorization.status, error: authorization.error } };
|
|
22
|
+
}
|
|
23
|
+
let capture;
|
|
24
|
+
const settle = () => setTimeout(async () => {
|
|
25
|
+
try {
|
|
26
|
+
if (capture) {
|
|
27
|
+
await operatorClient.capture(capture);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await operatorClient.release(voucher);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
onSettleError(error, capture ? 'Failed to capture payment' : 'Failed to release payment');
|
|
35
|
+
}
|
|
36
|
+
}, 0);
|
|
37
|
+
let result;
|
|
38
|
+
try {
|
|
39
|
+
result = await call();
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
settle();
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
if (!result.error && !isInferenceVoucher(voucher)) {
|
|
46
|
+
capture = { voucher };
|
|
47
|
+
}
|
|
48
|
+
else if (!result.error && result.usage && isInferenceVoucher(voucher)) {
|
|
49
|
+
// The function may report more output than it was allowed; the caller only signed for maxTokens.
|
|
50
|
+
const usage = { inputTokens: result.usage.inputTokens, outputTokens: Math.min(result.usage.outputTokens, voucher.maxTokens) };
|
|
51
|
+
result.usage = usage;
|
|
52
|
+
// A free call has nothing to charge, so its hold is released instead.
|
|
53
|
+
if (cappedInferenceCharge(voucher, usage) > 0) {
|
|
54
|
+
capture = { voucher, usage };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
settle();
|
|
58
|
+
return result;
|
|
59
|
+
}
|