@actuarial-ts/agents 0.2.0 → 0.3.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 +4 -2
- package/dist/advisor.d.ts +2 -1
- package/dist/advisor.d.ts.map +1 -1
- package/dist/advisor.js +3 -1
- package/dist/advisor.js.map +1 -1
- package/dist/divergence.d.ts.map +1 -1
- package/dist/divergence.js +6 -1
- package/dist/divergence.js.map +1 -1
- package/dist/judgment.d.ts +26 -0
- package/dist/judgment.d.ts.map +1 -1
- package/dist/judgment.js +21 -0
- package/dist/judgment.js.map +1 -1
- package/dist/mcp.d.ts +44 -9
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +57 -40
- package/dist/mcp.js.map +1 -1
- package/dist/promotion.d.ts +26 -0
- package/dist/promotion.d.ts.map +1 -1
- package/dist/promotion.js +49 -1
- package/dist/promotion.js.map +1 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +11 -1
- package/dist/remote.js.map +1 -1
- package/dist/tools.d.ts +87 -6
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +175 -15
- package/dist/tools.js.map +1 -1
- package/package.json +7 -5
- package/src/advisor.ts +141 -0
- package/src/divergence.ts +642 -0
- package/src/errors.ts +62 -0
- package/src/evals.ts +162 -0
- package/src/index.ts +9 -0
- package/src/judgment.ts +458 -0
- package/src/mcp.ts +274 -0
- package/src/promotion.ts +1240 -0
- package/src/remote.ts +371 -0
- package/src/tools.ts +561 -0
package/src/remote.ts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote-method tools (interchange spec rev 2.1, section 7 client side):
|
|
3
|
+
* defineRemoteMethod wraps one sidecar method (POST /v1/run/{method}) in
|
|
4
|
+
* defineActuarialTool, so a chainladder-python run becomes ordinary advisor
|
|
5
|
+
* evidence — enveloped, tenant-sealed, abortable.
|
|
6
|
+
*
|
|
7
|
+
* Contract decisions (each is load-bearing):
|
|
8
|
+
*
|
|
9
|
+
* - THE WIRE IS THE INTERCHANGE SPEC. The model-facing input schema is the
|
|
10
|
+
* sidecar request body MINUS engagementRef and minus any tenant field:
|
|
11
|
+
* { triangles: { primary, secondary? }, selection?, exposure?,
|
|
12
|
+
* parameters?, seed? }. Embedded documents are validated CLIENT-side via
|
|
13
|
+
* interchange parseDocument (integrity verified) before anything leaves
|
|
14
|
+
* the process, and the response document is parsed the same way — a 2xx
|
|
15
|
+
* body that fails parseDocument is refused with
|
|
16
|
+
* AgentsError("REMOTE_RESULT_INVALID"), never handed to the model.
|
|
17
|
+
*
|
|
18
|
+
* - THE TENANT SEAM IS UNTOUCHED. The input schema declares no tenant key
|
|
19
|
+
* (defineActuarialTool lints it at definition time), the wire body is
|
|
20
|
+
* built ONLY from the declared slots, and no engagementRef surface exists
|
|
21
|
+
* on the tool at all — the remote call carries zero correlation identity.
|
|
22
|
+
* The sidecar's own data-level lint (TENANT_KEY_REJECTED) backstops any
|
|
23
|
+
* tenant key hiding inside a supplied document's extensions.
|
|
24
|
+
*
|
|
25
|
+
* - PARAMETERS ARE AN EXPLICIT VOCABULARY, NOT A RECORD. The tenant lint
|
|
26
|
+
* rejects z.record / .passthrough (arbitrary-key smuggling surfaces), so
|
|
27
|
+
* the schema declares the sidecar's documented knobs explicitly; the
|
|
28
|
+
* sidecar 422s anything a given method does not accept, which keeps the
|
|
29
|
+
* two vocabularies honest against each other.
|
|
30
|
+
*
|
|
31
|
+
* - ERRORS MAP TO ENVELOPES WITH THE SIDECAR'S OWN CODES. A non-2xx with a
|
|
32
|
+
* schema'd { error: { code, message } } body keeps that code verbatim
|
|
33
|
+
* (UNAUTHORIZED, MISSING_SEED, REPLAY_REFUSED, ...); transport failures
|
|
34
|
+
* get SIDECAR_UNREACHABLE, the client-side deadline SIDECAR_TIMEOUT, and
|
|
35
|
+
* a caller abort ABORTED. The model always receives an envelope it can
|
|
36
|
+
* recover from, never a thrown exception.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { z } from "zod";
|
|
40
|
+
import {
|
|
41
|
+
parseDocument,
|
|
42
|
+
type MethodResultDoc,
|
|
43
|
+
type StochasticResultDoc,
|
|
44
|
+
} from "@actuarial-ts/interchange";
|
|
45
|
+
import { AgentsError } from "./errors.js";
|
|
46
|
+
import {
|
|
47
|
+
defineActuarialTool,
|
|
48
|
+
envelopeFailure,
|
|
49
|
+
type ActuarialToolContext,
|
|
50
|
+
type ToolEnvelopeFailure,
|
|
51
|
+
} from "./tools.js";
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Wire schema (model-facing): the spec-7 request body minus engagementRef
|
|
55
|
+
// and minus any tenant surface.
|
|
56
|
+
|
|
57
|
+
/** BF/Benktander/CapeCod apriori base: one value per origin label. */
|
|
58
|
+
const exposureSchema = z.object({
|
|
59
|
+
origins: z.array(z.string().min(1)).min(1).describe("Origin labels, matching the primary triangle's origins exactly"),
|
|
60
|
+
values: z.array(z.number().finite()).min(1).describe("One exposure value per origin, same order as origins"),
|
|
61
|
+
kind: z.string().min(1).describe('Exposure kind, e.g. "earnedPremium"'),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The sidecar's documented parameter vocabulary, declared explicitly (the
|
|
66
|
+
* tenant lint forbids open-key records by design). Every key is nullable:
|
|
67
|
+
* null means "not sent", and each METHOD accepts only its own subset — the
|
|
68
|
+
* sidecar refuses unknown keys per method with a 422 the envelope relays.
|
|
69
|
+
*/
|
|
70
|
+
const parametersSchema = z.object({
|
|
71
|
+
average: z
|
|
72
|
+
.enum(["volume", "simple", "regression"])
|
|
73
|
+
.nullable()
|
|
74
|
+
.describe("Development average when NO selection document is supplied (conflicts with selection)"),
|
|
75
|
+
n_periods: z
|
|
76
|
+
.number()
|
|
77
|
+
.int()
|
|
78
|
+
.nullable()
|
|
79
|
+
.describe("Development periods when NO selection is supplied: -1 = all periods (conflicts with selection)"),
|
|
80
|
+
strictness: z
|
|
81
|
+
.enum(["warn", "strict"])
|
|
82
|
+
.nullable()
|
|
83
|
+
.describe("Selection-replay strictness: warn (default) runs compromises with warnings; strict refuses them with 422"),
|
|
84
|
+
sigma_interpolation: z
|
|
85
|
+
.enum(["log-linear", "mack"])
|
|
86
|
+
.nullable()
|
|
87
|
+
.describe('MackChainladder sigma extrapolation; the mack1993-vw profile requires "mack" (engine default is log-linear)'),
|
|
88
|
+
apriori: z.number().nullable().describe("BornhuetterFerguson/Benktander a-priori multiplier on the exposure base"),
|
|
89
|
+
n_iters: z.number().int().nullable().describe("Benktander iterations (1 = BF)"),
|
|
90
|
+
trend: z.number().nullable().describe("CapeCod trend rate"),
|
|
91
|
+
decay: z.number().nullable().describe("CapeCod decay factor"),
|
|
92
|
+
growth: z
|
|
93
|
+
.enum(["loglogistic", "weibull"])
|
|
94
|
+
.nullable()
|
|
95
|
+
.describe("ClarkLDF growth curve family"),
|
|
96
|
+
n_sims: z.number().int().nullable().describe("BootstrapODPSample simulation count (default 1000, max 100000)"),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const remoteRunInputSchema = z.object({
|
|
100
|
+
triangles: z.object({
|
|
101
|
+
primary: z
|
|
102
|
+
.unknown()
|
|
103
|
+
.describe("The primary TriangleDoc: a full actuarial-interchange triangle document, integrity tag intact"),
|
|
104
|
+
secondary: z
|
|
105
|
+
.unknown()
|
|
106
|
+
.nullable()
|
|
107
|
+
.describe("Second TriangleDoc (MunichAdjustment's incurred slot; primary = paid). Null for every other method"),
|
|
108
|
+
}),
|
|
109
|
+
selection: z
|
|
110
|
+
.unknown()
|
|
111
|
+
.nullable()
|
|
112
|
+
.describe(
|
|
113
|
+
"SelectionDoc replayed as the development intent (spec 3.2). Null = engine-native factors from parameters. Supplying BOTH a selection and average/n_periods parameters is refused",
|
|
114
|
+
),
|
|
115
|
+
exposure: exposureSchema
|
|
116
|
+
.nullable()
|
|
117
|
+
.describe("Apriori exposure base — required by BornhuetterFerguson, Benktander, and CapeCod; null otherwise"),
|
|
118
|
+
parameters: parametersSchema.nullable().describe("Method-specific knobs; null sends none"),
|
|
119
|
+
seed: z
|
|
120
|
+
.number()
|
|
121
|
+
.int()
|
|
122
|
+
.nullable()
|
|
123
|
+
.describe("BootstrapODPSample only (REQUIRED there); deterministic methods refuse a supplied seed"),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export type RemoteRunInput = z.infer<typeof remoteRunInputSchema>;
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// The HTTP call (shared by defineRemoteMethod and host-authored tools)
|
|
130
|
+
|
|
131
|
+
/** Result-document kinds a sidecar run may return. */
|
|
132
|
+
const RESULT_KINDS = new Set(["method-result", "stochastic-result"]);
|
|
133
|
+
|
|
134
|
+
export interface RemoteMethodCallOptions {
|
|
135
|
+
/** Sidecar base URL, e.g. http://127.0.0.1:8091 (no trailing /v1). */
|
|
136
|
+
sidecarUrl: string;
|
|
137
|
+
/** The spec-7 method name, e.g. "Chainladder" or "MackChainladder". */
|
|
138
|
+
method: string;
|
|
139
|
+
/** Client-side deadline for one run. Default 60000 ms. */
|
|
140
|
+
timeoutMs?: number;
|
|
141
|
+
/** Extra request headers — the bearer token travels here, e.g.
|
|
142
|
+
* { authorization: "Bearer <SIDECAR_TOKEN>" }. */
|
|
143
|
+
headers?: Record<string, string>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface RemoteMethodSuccess {
|
|
147
|
+
success: true;
|
|
148
|
+
/** "method-result" | "stochastic-result" (what the run returned). */
|
|
149
|
+
kind: "method-result" | "stochastic-result";
|
|
150
|
+
/** The parsed, integrity-verified result document. */
|
|
151
|
+
doc: MethodResultDoc | StochasticResultDoc;
|
|
152
|
+
/** Reader-side warnings from parseDocument (normally empty). */
|
|
153
|
+
parseWarnings: string[];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type RemoteMethodResult = RemoteMethodSuccess | ToolEnvelopeFailure;
|
|
157
|
+
|
|
158
|
+
function isAbortLike(err: unknown, name: string): boolean {
|
|
159
|
+
return (err as { name?: unknown } | null)?.name === name;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* POSTs one spec-7 wire body to the sidecar and parses the response through
|
|
164
|
+
* interchange parseDocument. Never throws for transport/protocol failures —
|
|
165
|
+
* it returns the failure envelope directly, carrying the sidecar's own error
|
|
166
|
+
* code when the response body is schema'd. The ONE registered throw-shaped
|
|
167
|
+
* failure, REMOTE_RESULT_INVALID (a 2xx body that is not a verifiable
|
|
168
|
+
* result document), is also returned enveloped.
|
|
169
|
+
*/
|
|
170
|
+
export async function callRemoteMethod(
|
|
171
|
+
options: RemoteMethodCallOptions,
|
|
172
|
+
body: Record<string, unknown>,
|
|
173
|
+
signal?: AbortSignal,
|
|
174
|
+
): Promise<RemoteMethodResult> {
|
|
175
|
+
const timeoutMs = options.timeoutMs ?? 60_000;
|
|
176
|
+
const url = `${options.sidecarUrl.replace(/\/+$/, "")}/v1/run/${encodeURIComponent(options.method)}`;
|
|
177
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
178
|
+
const combined = signal !== undefined ? AbortSignal.any([signal, timeout]) : timeout;
|
|
179
|
+
|
|
180
|
+
let response: Response;
|
|
181
|
+
try {
|
|
182
|
+
response = await fetch(url, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "content-type": "application/json", ...(options.headers ?? {}) },
|
|
185
|
+
body: JSON.stringify(body),
|
|
186
|
+
signal: combined,
|
|
187
|
+
});
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// fetch rejects with the abort REASON: AbortSignal.timeout's reason is a
|
|
190
|
+
// TimeoutError, a caller abort is an AbortError; anything else is the
|
|
191
|
+
// transport itself failing. Node may also wrap the reason in a TypeError
|
|
192
|
+
// whose cause carries the story — the signal states disambiguate.
|
|
193
|
+
if (signal?.aborted === true && !timeout.aborted) {
|
|
194
|
+
return { success: false, error: { code: "ABORTED", message: "the caller aborted the sidecar call" } };
|
|
195
|
+
}
|
|
196
|
+
if (timeout.aborted || isAbortLike(err, "TimeoutError")) {
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
error: {
|
|
200
|
+
code: "SIDECAR_TIMEOUT",
|
|
201
|
+
message: `the sidecar did not answer ${options.method} within ${timeoutMs} ms`,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (isAbortLike(err, "AbortError")) {
|
|
206
|
+
return { success: false, error: { code: "ABORTED", message: "the caller aborted the sidecar call" } };
|
|
207
|
+
}
|
|
208
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
209
|
+
const cause = (err as { cause?: { message?: unknown } } | null)?.cause?.message;
|
|
210
|
+
return {
|
|
211
|
+
success: false,
|
|
212
|
+
error: {
|
|
213
|
+
code: "SIDECAR_UNREACHABLE",
|
|
214
|
+
message: `could not reach the sidecar at ${url}: ${typeof cause === "string" ? cause : detail}`,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let payload: unknown;
|
|
220
|
+
let bodyText = "";
|
|
221
|
+
try {
|
|
222
|
+
bodyText = await response.text();
|
|
223
|
+
payload = JSON.parse(bodyText);
|
|
224
|
+
} catch {
|
|
225
|
+
payload = undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
const schemad = (payload as { error?: { code?: unknown; message?: unknown } } | undefined)?.error;
|
|
230
|
+
const code = typeof schemad?.code === "string" && schemad.code.length > 0
|
|
231
|
+
? schemad.code
|
|
232
|
+
: `SIDECAR_HTTP_${response.status}`;
|
|
233
|
+
const message = typeof schemad?.message === "string" && schemad.message.length > 0
|
|
234
|
+
? schemad.message
|
|
235
|
+
: `sidecar answered HTTP ${response.status} for ${options.method}`;
|
|
236
|
+
return { success: false, error: { code, message } };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
if (payload === undefined) {
|
|
241
|
+
throw new Error(`response body is not JSON: ${bodyText.slice(0, 200)}`);
|
|
242
|
+
}
|
|
243
|
+
const { doc, warnings } = parseDocument(payload); // strictness "refuse": a broken tag throws
|
|
244
|
+
if (!RESULT_KINDS.has(doc.kind)) {
|
|
245
|
+
throw new Error(`expected a method-result or stochastic-result document, got kind "${doc.kind}"`);
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
success: true,
|
|
249
|
+
kind: doc.kind as "method-result" | "stochastic-result",
|
|
250
|
+
doc: doc as MethodResultDoc | StochasticResultDoc,
|
|
251
|
+
parseWarnings: warnings,
|
|
252
|
+
};
|
|
253
|
+
} catch (err) {
|
|
254
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
255
|
+
return envelopeFailure(
|
|
256
|
+
new AgentsError(
|
|
257
|
+
"REMOTE_RESULT_INVALID",
|
|
258
|
+
`the sidecar's 2xx response for ${options.method} is not a verifiable interchange result document: ${detail}`,
|
|
259
|
+
),
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// The tool factory
|
|
266
|
+
|
|
267
|
+
export interface DefineRemoteMethodOptions extends RemoteMethodCallOptions {
|
|
268
|
+
id: string;
|
|
269
|
+
description: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** The context slice the remote tool consumes beyond the tenant seam:
|
|
273
|
+
* Mastra's per-call abort signal (verified against @mastra/core 1.49's
|
|
274
|
+
* ToolExecutionContext), forwarded into fetch so a cancelled agent run
|
|
275
|
+
* cancels the sidecar call. */
|
|
276
|
+
interface AbortableToolContext extends ActuarialToolContext {
|
|
277
|
+
abortSignal?: AbortSignal;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Client-side guard: an embedded document slot must parse (integrity
|
|
281
|
+
* verified) AND be of the expected kind before it goes on the wire. Returns
|
|
282
|
+
* the failure envelope for a wrong kind (mirroring the sidecar's own
|
|
283
|
+
* WRONG_DOCUMENT_KIND vocabulary); parse failures throw ReservingError
|
|
284
|
+
* BAD_INTERCHANGE, which the tool wrapper envelopes with that code. */
|
|
285
|
+
function checkDocumentSlot(
|
|
286
|
+
raw: unknown,
|
|
287
|
+
slot: string,
|
|
288
|
+
expectedKind: string,
|
|
289
|
+
): ToolEnvelopeFailure | null {
|
|
290
|
+
const { doc } = parseDocument(raw);
|
|
291
|
+
if (doc.kind !== expectedKind) {
|
|
292
|
+
return {
|
|
293
|
+
success: false,
|
|
294
|
+
error: {
|
|
295
|
+
code: "WRONG_DOCUMENT_KIND",
|
|
296
|
+
message: `'${slot}' must be a ${expectedKind} document, got kind "${doc.kind}"`,
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function compactParameters(
|
|
304
|
+
parameters: z.infer<typeof parametersSchema> | null | undefined,
|
|
305
|
+
): Record<string, unknown> | null {
|
|
306
|
+
if (parameters === null || parameters === undefined) return null;
|
|
307
|
+
const out: Record<string, unknown> = {};
|
|
308
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
309
|
+
if (value !== null && value !== undefined) out[key] = value;
|
|
310
|
+
}
|
|
311
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Defines a read-kind actuarial tool that runs ONE sidecar method. The
|
|
316
|
+
* input schema is the spec-7 wire shape (minus engagementRef/tenant
|
|
317
|
+
* anything); execute validates the embedded documents, POSTs the wire body,
|
|
318
|
+
* forwards the Mastra abort signal, and returns either the parsed
|
|
319
|
+
* integrity-verified result document or a failure envelope carrying the
|
|
320
|
+
* sidecar's own error code.
|
|
321
|
+
*/
|
|
322
|
+
export function defineRemoteMethod(options: DefineRemoteMethodOptions) {
|
|
323
|
+
const { id, description, ...call } = options;
|
|
324
|
+
return defineActuarialTool({
|
|
325
|
+
id,
|
|
326
|
+
description,
|
|
327
|
+
kind: "read",
|
|
328
|
+
inputSchema: remoteRunInputSchema,
|
|
329
|
+
// The document slots are z.unknown() ON PURPOSE: each is a whole
|
|
330
|
+
// interchange document, validated at execute time by checkDocumentSlot ->
|
|
331
|
+
// parseDocument (schema + integrity tag), and interchange documents carry
|
|
332
|
+
// no tenant identifiers by spec section 12. Declaring the paths here keeps
|
|
333
|
+
// the tenant lint fail-closed for everything else.
|
|
334
|
+
allowUninspected: ["input.triangles.primary", "input.triangles.secondary", "input.selection"],
|
|
335
|
+
// Tenant-free BY DESIGN: the sidecar is stateless and the wire body is
|
|
336
|
+
// spec-7 minus engagementRef minus any tenant surface (see the file
|
|
337
|
+
// header). There is no tenant-scoped read or write on this path.
|
|
338
|
+
tenant: "none",
|
|
339
|
+
execute: async (input, _tenant, context): Promise<RemoteMethodResult> => {
|
|
340
|
+
const badPrimary = checkDocumentSlot(input.triangles.primary, "triangles.primary", "triangle");
|
|
341
|
+
if (badPrimary !== null) return badPrimary;
|
|
342
|
+
if (input.triangles.secondary !== null && input.triangles.secondary !== undefined) {
|
|
343
|
+
const badSecondary = checkDocumentSlot(input.triangles.secondary, "triangles.secondary", "triangle");
|
|
344
|
+
if (badSecondary !== null) return badSecondary;
|
|
345
|
+
}
|
|
346
|
+
if (input.selection !== null && input.selection !== undefined) {
|
|
347
|
+
const badSelection = checkDocumentSlot(input.selection, "selection", "selection");
|
|
348
|
+
if (badSelection !== null) return badSelection;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const parameters = compactParameters(input.parameters);
|
|
352
|
+
const body: Record<string, unknown> = {
|
|
353
|
+
triangles: {
|
|
354
|
+
primary: input.triangles.primary,
|
|
355
|
+
...(input.triangles.secondary !== null && input.triangles.secondary !== undefined
|
|
356
|
+
? { secondary: input.triangles.secondary }
|
|
357
|
+
: {}),
|
|
358
|
+
},
|
|
359
|
+
...(input.selection !== null && input.selection !== undefined
|
|
360
|
+
? { selection: input.selection }
|
|
361
|
+
: {}),
|
|
362
|
+
...(input.exposure !== null && input.exposure !== undefined
|
|
363
|
+
? { exposure: input.exposure }
|
|
364
|
+
: {}),
|
|
365
|
+
...(parameters !== null ? { parameters } : {}),
|
|
366
|
+
...(input.seed !== null && input.seed !== undefined ? { seed: input.seed } : {}),
|
|
367
|
+
};
|
|
368
|
+
return callRemoteMethod(call, body, (context as AbortableToolContext).abortSignal);
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
}
|