@voltro/ai 0.73.0 → 0.75.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/CHANGELOG.md +449 -0
- package/dist/index.d.ts +238 -1
- package/dist/index.js +625 -537
- package/dist/{inferenceQueue-CXEVU6mI.js → inferenceQueue-CdWTzRp7.js} +310 -229
- package/dist/workflow.d.ts +26 -6
- package/dist/workflow.js +80 -79
- package/package.json +5 -5
package/dist/workflow.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataStore } from '@voltro/database';
|
|
2
2
|
import { Effect } from 'effect';
|
|
3
|
+
import { PluginMediaPersistMetadata } from '@voltro/protocol';
|
|
3
4
|
import { Runtime } from 'effect';
|
|
4
5
|
import { Schema } from 'effect';
|
|
5
6
|
import { StepRetryPolicy } from '@voltro/workflow';
|
|
@@ -244,16 +245,23 @@ declare type GatewayModel = `${GatewayCreator}/${string}`;
|
|
|
244
245
|
*
|
|
245
246
|
* The queue row is the job's identity, allocated before the first paid call.
|
|
246
247
|
* A replay finds the row; a dispatcher that dies mid-job leaves a lease that
|
|
247
|
-
* expires — and the reclaiming dispatcher does NOT submit again
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
248
|
+
* expires — and the reclaiming dispatcher does NOT submit again. With a
|
|
249
|
+
* provider registered through `registerMediaTaskProvider` the row also
|
|
250
|
+
* carries the provider's task handle, so the reclaim polls the same task and
|
|
251
|
+
* the run receives its result. A built-in generator has no lookup — and a
|
|
252
|
+
* task job whose handle never reached the row cannot be found either — so
|
|
253
|
+
* those are reported to the run as `unknown` (`AiOffloadError.outcome`)
|
|
254
|
+
* rather than paid for twice. Artifacts are persisted through the storage
|
|
255
|
+
* plugin's `mediaPersist` capability under `<row>#<attempt>#<index>`, and
|
|
256
|
+
* the run receives URLs and ref ids.
|
|
252
257
|
*/
|
|
253
258
|
export declare const mediaStep: (options: MediaStepOptions) => Effect.Effect<MediaStepResult, unknown>;
|
|
254
259
|
|
|
255
|
-
export declare interface MediaStepOptions extends Omit<AiStepBaseOptions, 'system' | 'promptTemplate' | 'promptVars' | 'offload' | 'tools' | 'maxSteps' | 'toolContext' | 'fallbacks' | 'maxTokens'> {
|
|
260
|
+
export declare interface MediaStepOptions extends Omit<AiStepBaseOptions, 'system' | 'promptTemplate' | 'promptVars' | 'offload' | 'tools' | 'maxSteps' | 'toolContext' | 'fallbacks' | 'maxTokens' | 'provider'> {
|
|
256
261
|
readonly modality: 'image' | 'video' | 'speech';
|
|
262
|
+
/** A built-in provider, or the name of one registered with
|
|
263
|
+
* `registerMediaTaskProvider` — the dispatcher routes by the name. */
|
|
264
|
+
readonly provider?: ProviderConfig | RegisteredMediaTaskConfig;
|
|
257
265
|
/** Provider parameters — `n`, `size`, `aspectRatio`, `seed` for an image;
|
|
258
266
|
* `durationSeconds`, `resolution`, `fps` for a video; `voice`, `format` for
|
|
259
267
|
* speech. Passed through to the generator, journaled with the job. */
|
|
@@ -263,6 +271,9 @@ export declare interface MediaStepOptions extends Omit<AiStepBaseOptions, 'syste
|
|
|
263
271
|
readonly inputs?: Record<string, unknown>;
|
|
264
272
|
/** Required: a media job is always an external job with a durable identity. */
|
|
265
273
|
readonly store: DataStore;
|
|
274
|
+
/** Who the stored artifacts belong to, how they are served, and the tags they
|
|
275
|
+
* are found by — handed to the storage plugin's `mediaPersist` as-is. */
|
|
276
|
+
readonly persist?: PluginMediaPersistMetadata;
|
|
266
277
|
}
|
|
267
278
|
|
|
268
279
|
export declare interface MediaStepResult {
|
|
@@ -490,6 +501,15 @@ declare type ProviderConfig = {
|
|
|
490
501
|
*/
|
|
491
502
|
declare type ProviderFallbacks = ReadonlyArray<ProviderConfig>;
|
|
492
503
|
|
|
504
|
+
/**
|
|
505
|
+
* The provider config `mediaStep` accepts for a registered task provider —
|
|
506
|
+
* named, not enumerated, exactly like `RegisteredSpeechConfig`.
|
|
507
|
+
*/
|
|
508
|
+
declare interface RegisteredMediaTaskConfig {
|
|
509
|
+
readonly name: string;
|
|
510
|
+
readonly model?: string;
|
|
511
|
+
}
|
|
512
|
+
|
|
493
513
|
/** What `.render(vars)` produces — a stamp plus the text to send. */
|
|
494
514
|
declare interface RenderedPrompt extends PromptStamp {
|
|
495
515
|
readonly prompt: string;
|
package/dist/workflow.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as e } from "./provider-Cts1BRhY.js";
|
|
2
|
-
import { D as t,
|
|
2
|
+
import { D as t, O as n, T as r, V as i, b as a, k as o, m as s, o as c, p as l, q as u, s as d, t as f, tt as p, y as m } from "./inferenceQueue-CdWTzRp7.js";
|
|
3
3
|
import { Effect as h, Schema as g } from "effect";
|
|
4
4
|
import { eq as _ } from "@voltro/database";
|
|
5
5
|
import { CurrentWorkflowExecutionId as v, awaitSignalSuspending as y, step as b, suspendForBudget as x } from "@voltro/workflow";
|
|
@@ -24,14 +24,14 @@ var S = {
|
|
|
24
24
|
...t === void 0 ? {} : { system: t }
|
|
25
25
|
} : {
|
|
26
26
|
...i,
|
|
27
|
-
promptDigest:
|
|
27
|
+
promptDigest: s(e),
|
|
28
28
|
promptChars: e.length,
|
|
29
29
|
...t === void 0 ? {} : {
|
|
30
|
-
systemDigest:
|
|
30
|
+
systemDigest: s(t),
|
|
31
31
|
systemChars: t.length
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
}, E = (e) =>
|
|
34
|
+
}, E = (e) => l(e.prompt) ? {
|
|
35
35
|
text: e.prompt.prompt,
|
|
36
36
|
system: e.system ?? e.prompt.system,
|
|
37
37
|
rendered: e.prompt
|
|
@@ -40,22 +40,22 @@ var S = {
|
|
|
40
40
|
system: e.system,
|
|
41
41
|
rendered: void 0
|
|
42
42
|
}, D = (e) => ({
|
|
43
|
-
provider: String(e.
|
|
43
|
+
provider: String(e.name),
|
|
44
44
|
model: String(e.model ?? "unknown")
|
|
45
|
-
}), O = (e, t, n, r,
|
|
46
|
-
let
|
|
47
|
-
if (
|
|
48
|
-
let { provider:
|
|
49
|
-
return
|
|
50
|
-
provider:
|
|
51
|
-
model:
|
|
45
|
+
}), O = (e, t, n, r, a) => {
|
|
46
|
+
let o = e.store;
|
|
47
|
+
if (o === void 0) return h.void;
|
|
48
|
+
let { provider: s, model: c } = D(t), l = i(r.providerMetadata);
|
|
49
|
+
return u(o, {
|
|
50
|
+
provider: s,
|
|
51
|
+
model: c,
|
|
52
52
|
operation: n,
|
|
53
53
|
agent: e.name,
|
|
54
54
|
usage: r.usage,
|
|
55
55
|
...e.tenantId === void 0 ? {} : { tenantId: e.tenantId },
|
|
56
|
-
...
|
|
57
|
-
...
|
|
58
|
-
})
|
|
56
|
+
...l === void 0 ? {} : { actualCostUsd: l },
|
|
57
|
+
...a === void 0 ? {} : { prompt: a }
|
|
58
|
+
});
|
|
59
59
|
}, k = (e, t) => {
|
|
60
60
|
let n = e.store;
|
|
61
61
|
return n === void 0 || t === void 0 ? h.void : m(n, {
|
|
@@ -80,11 +80,11 @@ var S = {
|
|
|
80
80
|
}, P = (e, t, n, r, i, a, o) => h.gen(function* () {
|
|
81
81
|
let s = e.store;
|
|
82
82
|
if (s === void 0) return yield* h.die(new N(e.name));
|
|
83
|
-
let
|
|
84
|
-
if (
|
|
85
|
-
let { provider: u, model: p } = D(t), m = d(
|
|
83
|
+
let l = yield* v;
|
|
84
|
+
if (l === void 0) return yield* h.die(/* @__PURE__ */ Error(`aiStep('${e.name}'): offload: true is only valid inside a workflow body — there is no execution to suspend here.`));
|
|
85
|
+
let { provider: u, model: p } = D(t), m = d(l, e.name), g = yield* h.promise(() => s.query({
|
|
86
86
|
table: "_voltro_workflow_runs",
|
|
87
|
-
predicate: _("executionId",
|
|
87
|
+
predicate: _("executionId", l),
|
|
88
88
|
order: [],
|
|
89
89
|
take: 1,
|
|
90
90
|
skip: void 0,
|
|
@@ -93,10 +93,10 @@ var S = {
|
|
|
93
93
|
return yield* h.promise(() => s.insertIgnore(f, {
|
|
94
94
|
id: m,
|
|
95
95
|
tenantId: e.tenantId ?? null,
|
|
96
|
-
executionId:
|
|
96
|
+
executionId: l,
|
|
97
97
|
workflowName: y,
|
|
98
98
|
runId: typeof b == "string" ? b : null,
|
|
99
|
-
signalName:
|
|
99
|
+
signalName: c(m),
|
|
100
100
|
stepName: e.name,
|
|
101
101
|
status: "pending",
|
|
102
102
|
kind: n,
|
|
@@ -117,7 +117,7 @@ var S = {
|
|
|
117
117
|
enqueuedAt: /* @__PURE__ */ new Date()
|
|
118
118
|
}, { conflictColumns: ["id"] })), m;
|
|
119
119
|
}), F = (e, t, n, r, i, a, o, s) => h.gen(function* () {
|
|
120
|
-
let
|
|
120
|
+
let l = yield* b({
|
|
121
121
|
name: `${e.name}:enqueue`,
|
|
122
122
|
success: g.String,
|
|
123
123
|
input: i,
|
|
@@ -125,7 +125,7 @@ var S = {
|
|
|
125
125
|
});
|
|
126
126
|
yield* k(e, a.rendered);
|
|
127
127
|
let u = yield* y({ store: e.store }, {
|
|
128
|
-
name: l
|
|
128
|
+
name: c(l),
|
|
129
129
|
schema: g.Unknown,
|
|
130
130
|
timeoutMs: e.offloadTimeoutMs ?? 36e5
|
|
131
131
|
});
|
|
@@ -139,12 +139,12 @@ var S = {
|
|
|
139
139
|
if (t === void 0) return;
|
|
140
140
|
let n = e.store;
|
|
141
141
|
if (n === void 0) return yield* h.die(new I(e.name));
|
|
142
|
-
let r = e.tenantId ?? null, i = t.name ?? "ai-usd", a = yield* v,
|
|
142
|
+
let r = e.tenantId ?? null, i = t.name ?? "ai-usd", a = yield* v, o = () => p(n, {
|
|
143
143
|
tenantId: r,
|
|
144
144
|
limitUsd: t.limitUsd,
|
|
145
145
|
addUsd: 0,
|
|
146
146
|
...t.since === void 0 ? {} : { since: t.since }
|
|
147
|
-
}).pipe(h.as(null), h.catchTag("AiBudgetExceeded", (e) => h.succeed(e))),
|
|
147
|
+
}).pipe(h.as(null), h.catchTag("AiBudgetExceeded", (e) => h.succeed(e))), s = () => t.estimateUsd === void 0 ? h.void : p(n, {
|
|
148
148
|
tenantId: r,
|
|
149
149
|
limitUsd: t.limitUsd,
|
|
150
150
|
addUsd: t.estimateUsd,
|
|
@@ -154,20 +154,20 @@ var S = {
|
|
|
154
154
|
e.name
|
|
155
155
|
])) },
|
|
156
156
|
...t.since === void 0 ? {} : { since: t.since }
|
|
157
|
-
}).pipe(h.asVoid),
|
|
158
|
-
return
|
|
157
|
+
}).pipe(h.asVoid), c = yield* o();
|
|
158
|
+
return c === null ? yield* s() : (t.onExceeded ?? "fail") !== "suspend" || a === void 0 ? yield* h.fail(c) : (yield* x({
|
|
159
159
|
store: n,
|
|
160
160
|
budget: i,
|
|
161
161
|
tenantId: r,
|
|
162
162
|
executionId: a,
|
|
163
163
|
workflowName: yield* R(n, a),
|
|
164
164
|
stepName: e.name,
|
|
165
|
-
spentMicroUsd: Math.round(
|
|
165
|
+
spentMicroUsd: Math.round(c.spentUsd * 1e6),
|
|
166
166
|
limitMicroUsd: Math.round(t.limitUsd * 1e6),
|
|
167
|
-
hasHeadroom:
|
|
167
|
+
hasHeadroom: o().pipe(h.map((e) => e === null)),
|
|
168
168
|
...t.holdTimeoutMs === void 0 ? {} : { timeoutMs: t.holdTimeoutMs },
|
|
169
169
|
...t.recheckEveryMs === void 0 ? {} : { recheckEveryMs: t.recheckEveryMs }
|
|
170
|
-
}).pipe(h.catchAll((e) => h.die(e))), yield*
|
|
170
|
+
}).pipe(h.catchAll((e) => h.die(e))), yield* s());
|
|
171
171
|
}), R = (e, t) => h.promise(async () => {
|
|
172
172
|
let n = await e.query({
|
|
173
173
|
table: "_voltro_workflow_runs",
|
|
@@ -178,29 +178,29 @@ var S = {
|
|
|
178
178
|
projection: void 0
|
|
179
179
|
});
|
|
180
180
|
return String(n[0]?.tag ?? "");
|
|
181
|
-
}), z = (
|
|
182
|
-
let r =
|
|
181
|
+
}), z = (t) => {
|
|
182
|
+
let r = t.provider ?? e(), i = E(t), a = {
|
|
183
183
|
...D(r),
|
|
184
184
|
operation: "generateText",
|
|
185
|
-
offloaded:
|
|
186
|
-
...T(i.text, i.system,
|
|
187
|
-
}, o = (e) =>
|
|
188
|
-
return
|
|
189
|
-
name:
|
|
185
|
+
offloaded: t.offload === !0,
|
|
186
|
+
...T(i.text, i.system, t.recordPrompt ?? "digest", i.rendered)
|
|
187
|
+
}, o = (e) => t.budget === void 0 ? e : h.zipRight(L(t), e);
|
|
188
|
+
return t.offload === !0 ? o(F(t, r, "text", null, a, i)) : o(b({
|
|
189
|
+
name: t.name,
|
|
190
190
|
success: A,
|
|
191
191
|
input: a,
|
|
192
|
-
retry:
|
|
193
|
-
execute:
|
|
192
|
+
retry: t.retry ?? S,
|
|
193
|
+
execute: n({
|
|
194
194
|
prompt: i.text,
|
|
195
195
|
provider: r,
|
|
196
196
|
...i.system === void 0 ? {} : { system: i.system },
|
|
197
|
-
...
|
|
198
|
-
...
|
|
199
|
-
...
|
|
200
|
-
...
|
|
201
|
-
...
|
|
202
|
-
...
|
|
203
|
-
}).pipe(h.tap((e) => O(
|
|
197
|
+
...t.fallbacks === void 0 ? {} : { fallbacks: t.fallbacks },
|
|
198
|
+
...t.maxTokens === void 0 ? {} : { maxTokens: t.maxTokens },
|
|
199
|
+
...t.providerOptions === void 0 ? {} : { providerOptions: t.providerOptions },
|
|
200
|
+
...t.tools === void 0 ? {} : { tools: t.tools },
|
|
201
|
+
...t.maxSteps === void 0 ? {} : { maxSteps: t.maxSteps },
|
|
202
|
+
...t.toolContext === void 0 ? {} : { toolContext: t.toolContext }
|
|
203
|
+
}).pipe(h.tap((e) => O(t, r, "generateText", e, i.rendered)), h.tap(() => k(t, i.rendered)), h.map((e) => ({
|
|
204
204
|
text: e.text,
|
|
205
205
|
inputTokens: e.usage.inputTokens ?? null,
|
|
206
206
|
outputTokens: e.usage.outputTokens ?? null,
|
|
@@ -222,17 +222,17 @@ var S = {
|
|
|
222
222
|
...D(n),
|
|
223
223
|
operation: "transcribe",
|
|
224
224
|
offloaded: t.offload === !0
|
|
225
|
-
},
|
|
226
|
-
return t.offload === !0 ?
|
|
225
|
+
}, o = (e) => t.budget === void 0 ? e : h.zipRight(L(t), e);
|
|
226
|
+
return t.offload === !0 ? o(F(t, n, "transcription", null, i, {
|
|
227
227
|
text: "",
|
|
228
228
|
system: void 0,
|
|
229
229
|
rendered: void 0
|
|
230
|
-
}, r)) :
|
|
230
|
+
}, r)) : o(b({
|
|
231
231
|
name: t.name,
|
|
232
232
|
success: B,
|
|
233
233
|
input: i,
|
|
234
234
|
retry: t.retry ?? S,
|
|
235
|
-
execute:
|
|
235
|
+
execute: a({
|
|
236
236
|
...r,
|
|
237
237
|
provider: n,
|
|
238
238
|
...t.resolveRef === void 0 ? {} : { resolveRef: t.resolveRef }
|
|
@@ -249,7 +249,8 @@ var S = {
|
|
|
249
249
|
}), i = {
|
|
250
250
|
modality: t.modality,
|
|
251
251
|
params: t.params ?? {},
|
|
252
|
-
inputs: t.inputs ?? {}
|
|
252
|
+
inputs: t.inputs ?? {},
|
|
253
|
+
...t.persist === void 0 ? {} : { persist: t.persist }
|
|
253
254
|
}, a = {
|
|
254
255
|
...D(n),
|
|
255
256
|
operation: `generate${t.modality[0].toUpperCase()}${t.modality.slice(1)}`,
|
|
@@ -261,42 +262,42 @@ var S = {
|
|
|
261
262
|
...t,
|
|
262
263
|
offload: !0
|
|
263
264
|
}, n, "media", null, a, r, void 0, i));
|
|
264
|
-
}, U = (
|
|
265
|
-
let i =
|
|
265
|
+
}, U = (n) => {
|
|
266
|
+
let i = n.provider ?? e(), a = E(n), s = {
|
|
266
267
|
...D(i),
|
|
267
268
|
operation: "generateObject",
|
|
268
|
-
offloaded:
|
|
269
|
-
...T(a.text, a.system,
|
|
270
|
-
},
|
|
271
|
-
return
|
|
272
|
-
let e = yield*
|
|
273
|
-
return yield* g.decodeUnknown(
|
|
274
|
-
})) :
|
|
275
|
-
name:
|
|
276
|
-
success:
|
|
277
|
-
input:
|
|
278
|
-
retry:
|
|
279
|
-
execute: (
|
|
269
|
+
offloaded: n.offload === !0,
|
|
270
|
+
...T(a.text, a.system, n.recordPrompt ?? "digest", a.rendered)
|
|
271
|
+
}, c = (e) => n.budget === void 0 ? e : h.zipRight(L(n), e);
|
|
272
|
+
return n.offload === !0 ? c(h.gen(function* () {
|
|
273
|
+
let e = yield* o(n.schema, "aiObjectStep"), t = yield* F(n, i, "object", e, s, a);
|
|
274
|
+
return yield* g.decodeUnknown(n.schema)(t);
|
|
275
|
+
})) : c(b({
|
|
276
|
+
name: n.name,
|
|
277
|
+
success: n.schema,
|
|
278
|
+
input: s,
|
|
279
|
+
retry: n.retry ?? S,
|
|
280
|
+
execute: (n.tools === void 0 ? r({
|
|
280
281
|
prompt: a.text,
|
|
281
|
-
schema:
|
|
282
|
+
schema: n.schema,
|
|
282
283
|
provider: i,
|
|
283
284
|
...a.system === void 0 ? {} : { system: a.system },
|
|
284
|
-
...
|
|
285
|
-
...
|
|
286
|
-
...
|
|
287
|
-
}) :
|
|
285
|
+
...n.fallbacks === void 0 ? {} : { fallbacks: n.fallbacks },
|
|
286
|
+
...n.maxTokens === void 0 ? {} : { maxTokens: n.maxTokens },
|
|
287
|
+
...n.providerOptions === void 0 ? {} : { providerOptions: n.providerOptions }
|
|
288
|
+
}) : t({
|
|
288
289
|
prompt: a.text,
|
|
289
|
-
schema:
|
|
290
|
+
schema: n.schema,
|
|
290
291
|
provider: i,
|
|
291
|
-
tools:
|
|
292
|
-
...
|
|
293
|
-
...
|
|
292
|
+
tools: n.tools,
|
|
293
|
+
...n.maxSteps === void 0 ? {} : { maxSteps: n.maxSteps },
|
|
294
|
+
...n.toolContext === void 0 ? {} : { toolContext: n.toolContext },
|
|
294
295
|
...a.system === void 0 ? {} : { system: a.system },
|
|
295
|
-
...
|
|
296
|
-
...
|
|
297
|
-
...
|
|
298
|
-
})).pipe(h.tap((e) => O(
|
|
296
|
+
...n.fallbacks === void 0 ? {} : { fallbacks: n.fallbacks },
|
|
297
|
+
...n.maxTokens === void 0 ? {} : { maxTokens: n.maxTokens },
|
|
298
|
+
...n.providerOptions === void 0 ? {} : { providerOptions: n.providerOptions }
|
|
299
|
+
})).pipe(h.tap((e) => O(n, i, "generateObject", e, a.rendered)), h.tap(() => k(n, a.rendered)), h.map((e) => e.object))
|
|
299
300
|
}));
|
|
300
301
|
};
|
|
301
302
|
//#endregion
|
|
302
|
-
export { S as AI_STEP_RETRY, I as AiBudgetMisuseError, M as AiOffloadError, N as AiOffloadMisuseError, w as DEFAULT_AI_BUDGET_NAME, C as DEFAULT_OFFLOAD_TIMEOUT_MS, B as TranscriptionStepResult, U as aiObjectStep, z as aiStep, H as mediaStep,
|
|
303
|
+
export { S as AI_STEP_RETRY, I as AiBudgetMisuseError, M as AiOffloadError, N as AiOffloadMisuseError, w as DEFAULT_AI_BUDGET_NAME, C as DEFAULT_OFFLOAD_TIMEOUT_MS, B as TranscriptionStepResult, U as aiObjectStep, z as aiStep, H as mediaStep, s as promptDigest, T as promptRecordFor, E as resolveStepPrompt, V as transcribeStep };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "Effect-native AI primitives — generateText / generateObject / streamText, tools, agents, media generation, cost tracking + budgets, and provider fallback.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@ai-sdk/anthropic": "^4.0.49",
|
|
57
57
|
"@ai-sdk/openai": "^4.0.58",
|
|
58
|
-
"@voltro/cache": "0.
|
|
59
|
-
"@voltro/database": "0.
|
|
60
|
-
"@voltro/protocol": "0.
|
|
61
|
-
"@voltro/workflow": "0.
|
|
58
|
+
"@voltro/cache": "0.75.0",
|
|
59
|
+
"@voltro/database": "0.75.0",
|
|
60
|
+
"@voltro/protocol": "0.75.0",
|
|
61
|
+
"@voltro/workflow": "0.75.0",
|
|
62
62
|
"ajv": "^8.20.0",
|
|
63
63
|
"ajv-formats": "^3.0.1"
|
|
64
64
|
},
|