octaflow 0.30.0 → 0.32.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 +7 -1
- package/dist/ai/index.d.ts +153 -27
- package/dist/ai/index.js +157 -6
- package/dist/ai/providers/index.d.ts +1 -1
- package/dist/{dispatcher-ClQMXA9E.d.ts → dispatcher-B3ruILCN.d.ts} +1 -1
- package/dist/{hooks-mWaAKHqn.d.ts → hooks-C46TPBEg.d.ts} +1 -1
- package/dist/{index-Du05Lnwr.d.ts → index-D0XhoTkE.d.ts} +17 -10
- package/dist/index.d.ts +5 -5
- package/dist/index.js +14 -6
- package/dist/{observability-CkC4FRCp.d.ts → observability-0MLzBjuV.d.ts} +1 -1
- package/dist/pg-boss/index.d.ts +2 -2
- package/dist/postgres/index.d.ts +49 -2
- package/dist/postgres/index.js +53 -1
- package/dist/{provider-probe-l2Jmpuhr.d.ts → provider-probe-_rio4ykx.d.ts} +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -215,7 +215,7 @@ next to what your steps actually do.
|
|
|
215
215
|
| 🚑 | **Crash recovery** — a step whose worker died is re-queued while its attempt budget lasts |
|
|
216
216
|
| 💓 | **Heartbeats** — a long step proves it is alive, so a dead one is caught in seconds, not minutes |
|
|
217
217
|
| 🔭 | **Observability** — lifecycle events (run history), per-step spans, and `createMeterObserver` turning the events into OpenTelemetry-style metrics — all pluggable, all structural |
|
|
218
|
-
| 🤖 | **AI add-on** — instrumented models, token/cost capture, quota, daily rollups, refusal-aware models, lenient JSON from local models (`withJsonRepair`), a vendor's refusal explained by key and hint (`explainProviderError`), a per-run circuit breaker, a connection test (`testProvider`) |
|
|
218
|
+
| 🤖 | **AI add-on** — instrumented models, token/cost capture, quota, daily rollups, a `usage` sink that books each step into `@octabits-io/metering` (the paid APIs around the model in the same table, priced at read time), refusal-aware models, lenient JSON from local models (`withJsonRepair`), a vendor's refusal explained by key and hint (`explainProviderError`), a per-run circuit breaker, a connection test (`testProvider`) |
|
|
219
219
|
| 🧱 | **Pluggable everything** — `WorkflowStore`, `Dispatcher`, `StepGate`, `FlowObserver`, hooks |
|
|
220
220
|
|
|
221
221
|
---
|
|
@@ -254,6 +254,12 @@ pnpm add ai @ai-sdk/provider
|
|
|
254
254
|
a `schema`, and is meant to be pasted into your own migration system; `applySchema(pool, ddl)`
|
|
255
255
|
is the dev and test shortcut.
|
|
256
256
|
|
|
257
|
+
The stores, the gate and the event sink reach the database only through the `SqlExecutor` seam
|
|
258
|
+
(`query` plus a same-connection `transaction`). Three executors ship with it: `poolExecutor(pool)`
|
|
259
|
+
for `pg`, `postgresJsExecutor(sql)` for a postgres.js host (`transaction` is `sql.begin`), and
|
|
260
|
+
`pgliteExecutor(pglite)` for PGlite in tests. Wrap any of them to set row-level-security GUCs
|
|
261
|
+
first.
|
|
262
|
+
|
|
257
263
|
#### The Drizzle columns
|
|
258
264
|
|
|
259
265
|
The rule for every table-owning capability on the platform: `./postgres` ships the store over
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { M as TypedStepContext, N as TypedWorkflow, O as DefineStepConfig, St as Result, U as RetryPolicy, f as Decider, gt as JoinRule, j as TypedStep, xt as FlowErrorShape } from "../index-D0XhoTkE.js";
|
|
2
2
|
import { n as Logger } from "../logger-BCus3AOa.js";
|
|
3
|
-
import { i as BuildStepContextArgs, o as WorkflowHooks } from "../hooks-
|
|
3
|
+
import { i as BuildStepContextArgs, o as WorkflowHooks } from "../hooks-C46TPBEg.js";
|
|
4
4
|
import { C as isSubprocessorResidency, S as embeddingDimensionsFor, _ as EmbeddingProviderMeta, a as AiProcessingRegion, b as OpenAiCompatibleProviderId, c as AiProviderModelOption, d as AiVendorResidency, f as DEFAULT_AI_PROVIDER, g as EmbeddingProviderId, h as EMBEDDING_PROVIDER_IDS, i as AiEndpointResidency, l as AiResidencyError, m as EMBEDDING_PROVIDERS, n as AI_PROVIDER_IDS, o as AiProviderId, p as DEFAULT_EMBEDDING_PROVIDER, r as AiEndpointRegion, s as AiProviderMeta, t as AI_PROVIDERS, u as AiResidencyResult, v as OPENAI_COMPATIBLE_PROVIDER, w as resolveEndpointResidency, x as OpenAiCompatibleProviderMeta, y as OPENAI_COMPATIBLE_PROVIDER_ID } from "../provider-registry-C_mOfdHH.js";
|
|
5
|
-
import { A as maskKeys, C as ProviderErrorExplanation, D as explainProviderError, E as ProviderErrorPreset, O as isFatalProviderError, S as ProviderError, T as ProviderErrorPhrase, _ as ExplainProviderErrorOptions, a as OllamaProbeReport, b as PROVIDER_ERROR_REDACTED, c as ProviderProbeOptions, d as testProvider, f as CircuitBreaker, g as DEFAULT_PROVIDER_ERROR_DETAIL_LENGTH, h as withCircuitBreaker, i as OllamaProbeOptions, k as isProviderError, l as ProviderProbeReport, m as createCircuitBreaker, n as DEFAULT_PROVIDER_PROBE_PROMPT, o as ProbeableProvider, p as CircuitBreakerOptions, r as DEFAULT_PROVIDER_PROBE_TIMEOUT_MS, s as ProviderProbeFailure, t as DEFAULT_OLLAMA_PROBE_TIMEOUT_MS, u as testOllamaModel, v as FATAL_PROVIDER_ERROR_KEYS, w as ProviderErrorKey, x as PROVIDER_ERROR_STATUS, y as PROVIDER_ERROR_PHRASES } from "../provider-probe-
|
|
5
|
+
import { A as maskKeys, C as ProviderErrorExplanation, D as explainProviderError, E as ProviderErrorPreset, O as isFatalProviderError, S as ProviderError, T as ProviderErrorPhrase, _ as ExplainProviderErrorOptions, a as OllamaProbeReport, b as PROVIDER_ERROR_REDACTED, c as ProviderProbeOptions, d as testProvider, f as CircuitBreaker, g as DEFAULT_PROVIDER_ERROR_DETAIL_LENGTH, h as withCircuitBreaker, i as OllamaProbeOptions, k as isProviderError, l as ProviderProbeReport, m as createCircuitBreaker, n as DEFAULT_PROVIDER_PROBE_PROMPT, o as ProbeableProvider, p as CircuitBreakerOptions, r as DEFAULT_PROVIDER_PROBE_TIMEOUT_MS, s as ProviderProbeFailure, t as DEFAULT_OLLAMA_PROBE_TIMEOUT_MS, u as testOllamaModel, v as FATAL_PROVIDER_ERROR_KEYS, w as ProviderErrorKey, x as PROVIDER_ERROR_STATUS, y as PROVIDER_ERROR_PHRASES } from "../provider-probe-_rio4ykx.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
+
import { ToolSet } from "ai";
|
|
7
8
|
import { EmbeddingModelV4, LanguageModelV4, LanguageModelV4Usage, SharedV4ProviderMetadata } from "@ai-sdk/provider";
|
|
8
9
|
//#region src/ai/instrumented-model.d.ts
|
|
9
10
|
export interface AccumulatedUsage {
|
|
@@ -12,6 +13,8 @@ export interface AccumulatedUsage {
|
|
|
12
13
|
cacheReadTokens: number;
|
|
13
14
|
cacheWriteTokens: number;
|
|
14
15
|
modelId: string;
|
|
16
|
+
/** The provider the model belongs to (`model.provider`), when the call reported it. */
|
|
17
|
+
provider?: string;
|
|
15
18
|
}
|
|
16
19
|
export interface UsageAccumulator {
|
|
17
20
|
/** Record usage from a single LLM call (additive — supports multiple calls per step). */
|
|
@@ -123,35 +126,18 @@ export interface AiContext<THost = unknown> {
|
|
|
123
126
|
//#region src/ai/define-ai-step.d.ts
|
|
124
127
|
/** An AI workflow step — a flow-core `TypedStep` whose context is an `AiContext`. */
|
|
125
128
|
export type AiTypedStep<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput extends Record<string, unknown> = Record<string, unknown>, THost = unknown, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>> = {}> = TypedStep<TInput, TOutput, TDeps, AiContext<THost>>;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/** Optional retry policy (max attempts, backoff) — e.g. for provider 429s. */
|
|
133
|
-
retry?: RetryPolicy;
|
|
134
|
-
/** Optional per-step wall-clock timeout in ms. */
|
|
135
|
-
timeoutMs?: number;
|
|
136
|
-
/** Optional durable start delay in ms — held in the queue once the step is ready. */
|
|
137
|
-
delayMs?: number;
|
|
138
|
-
/**
|
|
139
|
-
* What a terminal failure does — see `defineStep`'s `onFailure`. `{ output }`
|
|
140
|
-
* completes the step with a stand-in and the DAG carries on; the model's
|
|
141
|
-
* refusal or outage then costs one section of the result, not the run.
|
|
142
|
-
*/
|
|
143
|
-
onFailure?: 'fail' | {
|
|
144
|
-
output: TOutput;
|
|
145
|
-
};
|
|
146
|
-
/** Resources the step draws on (the model, a vendor), for the admission gate — see `defineStep`. */
|
|
147
|
-
resources?: readonly string[];
|
|
148
|
-
}
|
|
129
|
+
/**
|
|
130
|
+
* `defineStep`'s config with the handler context fixed to `AiContext<THost>` —
|
|
131
|
+
* every option a step has (`when`, `join`, heartbeats, `onFailure`, …), so an
|
|
132
|
+
* AI step branches and converges like any other.
|
|
133
|
+
*/
|
|
134
|
+
export type DefineAiStepConfig<TInput extends Record<string, unknown>, TOutput extends Record<string, unknown>, THost, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>>, TJoin extends JoinRule = 'all'> = DefineStepConfig<TInput, TOutput, TDeps, AiContext<THost>, TJoin>;
|
|
149
135
|
/**
|
|
150
136
|
* Define a type-safe AI workflow step. Identical to flow-core's `defineStep`
|
|
151
137
|
* except the handler context is fixed to `AiContext<THost>`, so handlers get a
|
|
152
138
|
* ready-to-use `ctx.context.model` (instrumented) and `ctx.context.host`.
|
|
153
139
|
*/
|
|
154
|
-
export declare function defineAiStep<TInput extends Record<string, unknown>, TOutput extends Record<string, unknown>, THost = unknown, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>> = {}>(config: DefineAiStepConfig<TInput, TOutput, THost, TDeps>): AiTypedStep<TInput, TOutput, THost, TDeps>;
|
|
140
|
+
export declare function defineAiStep<TInput extends Record<string, unknown>, TOutput extends Record<string, unknown>, THost = unknown, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>> = {}, TJoin extends JoinRule = 'all'>(config: DefineAiStepConfig<TInput, TOutput, THost, TDeps, TJoin>): AiTypedStep<TInput, TOutput, THost, TDeps>;
|
|
155
141
|
/** Build a typed AI workflow from `defineAiStep` definitions. */
|
|
156
142
|
export declare function buildAiWorkflow<TInput extends Record<string, unknown>, THost = unknown, TSteps extends Record<string, TypedStep<any, any, any, AiContext<THost>>> = {}>(config: {
|
|
157
143
|
type: string;
|
|
@@ -184,6 +170,114 @@ export interface ModelDeciderOptions {
|
|
|
184
170
|
*/
|
|
185
171
|
export declare function createModelDecider<THost = unknown>(options?: ModelDeciderOptions): Decider<AiContext<THost>>;
|
|
186
172
|
//#endregion
|
|
173
|
+
//#region src/ai/agent.d.ts
|
|
174
|
+
/** What one agent run did — persisted with the answer, never parsed. */
|
|
175
|
+
export interface AgentTrace {
|
|
176
|
+
/** Model calls the loop made. */
|
|
177
|
+
rounds: number;
|
|
178
|
+
/** Tool names in the order they were called, the exit last. */
|
|
179
|
+
toolCalls: string[];
|
|
180
|
+
/** Input + output tokens across all rounds. */
|
|
181
|
+
tokens: number;
|
|
182
|
+
}
|
|
183
|
+
/** One way out of an agent: the model ends the run by calling it, and its arguments are the value. */
|
|
184
|
+
export interface AgentExit<T = unknown> {
|
|
185
|
+
description: string;
|
|
186
|
+
schema: z.ZodType<T>;
|
|
187
|
+
}
|
|
188
|
+
export type AgentExits = Record<string, AgentExit<any>>;
|
|
189
|
+
/** The exit an agent took, typed by its name. */
|
|
190
|
+
export type AgentExitTaken<TExits extends AgentExits> = { [K in keyof TExits & string]: {
|
|
191
|
+
exit: K;
|
|
192
|
+
value: TExits[K] extends AgentExit<infer T> ? T : never;
|
|
193
|
+
}; }[keyof TExits & string];
|
|
194
|
+
interface AgentLimits {
|
|
195
|
+
/** The model — in a step, `ctx.context.model`, so usage lands on the step. */
|
|
196
|
+
model: LanguageModelV4;
|
|
197
|
+
system: string;
|
|
198
|
+
prompt: string;
|
|
199
|
+
/** The tools the agent works with. Their names must not collide with an exit's. */
|
|
200
|
+
tools?: ToolSet;
|
|
201
|
+
/** Most model calls before the run gives up. Default 8. */
|
|
202
|
+
maxRounds?: number;
|
|
203
|
+
/** Most input + output tokens before the run gives up. Default: no cap. */
|
|
204
|
+
maxTokens?: number;
|
|
205
|
+
/**
|
|
206
|
+
* Most output tokens per model call, passed to the AI SDK. Default: the
|
|
207
|
+
* provider's — which on a local server can be small enough to cut a
|
|
208
|
+
* thinking model off before it calls anything.
|
|
209
|
+
*/
|
|
210
|
+
maxOutputTokens?: number;
|
|
211
|
+
/** Called after every round; `false` stops the run — pass the step's `heartbeat`. */
|
|
212
|
+
heartbeat?: () => Promise<boolean>;
|
|
213
|
+
abortSignal?: AbortSignal;
|
|
214
|
+
/** Passed to the AI SDK. Default 0 — the step's retry policy owns retries. */
|
|
215
|
+
maxRetries?: number;
|
|
216
|
+
}
|
|
217
|
+
export interface RunAgentToExitOptions<TExits extends AgentExits> extends AgentLimits {
|
|
218
|
+
exits: TExits;
|
|
219
|
+
}
|
|
220
|
+
export interface RunAgentOptions<TOutput> extends AgentLimits {
|
|
221
|
+
/** The answer's shape — the arguments of the one exit, `answer`. */
|
|
222
|
+
output: z.ZodType<TOutput>;
|
|
223
|
+
}
|
|
224
|
+
export interface AgentRun<TOutput> {
|
|
225
|
+
output: TOutput;
|
|
226
|
+
trace: AgentTrace;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Run one agent until it takes one of `exits`, or fails. Stopping on a limit
|
|
230
|
+
* before an exit is not retryable — the same budget would stop the same way —
|
|
231
|
+
* so the step fails, or takes its `onFailure` stand-in.
|
|
232
|
+
*/
|
|
233
|
+
export declare function runAgentToExit<TExits extends AgentExits>(options: RunAgentToExitOptions<TExits>): Promise<AgentExitTaken<TExits> & {
|
|
234
|
+
trace: AgentTrace;
|
|
235
|
+
}>;
|
|
236
|
+
/** Run one agent to a typed answer: `runAgentToExit` with the single exit `answer`. */
|
|
237
|
+
export declare function runAgent<TOutput>(options: RunAgentOptions<TOutput>): Promise<AgentRun<TOutput>>;
|
|
238
|
+
/** The trace as a schema — for a step output that carries one. */
|
|
239
|
+
export declare const AGENT_TRACE_SCHEMA: z.ZodObject<{
|
|
240
|
+
rounds: z.ZodNumber;
|
|
241
|
+
toolCalls: z.ZodArray<z.ZodString>;
|
|
242
|
+
tokens: z.ZodNumber;
|
|
243
|
+
}, z.core.$strip>;
|
|
244
|
+
interface DefineAgentStepConfig<TInput extends Record<string, unknown>, TAnswer extends Record<string, unknown>, THost, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>>> {
|
|
245
|
+
type: string;
|
|
246
|
+
workflowInputSchema: z.ZodType<TInput>;
|
|
247
|
+
/** The answer's shape — the step outputs `{ answer, trace }`. */
|
|
248
|
+
answerSchema: z.ZodType<TAnswer>;
|
|
249
|
+
dependencies?: TDeps;
|
|
250
|
+
system: string;
|
|
251
|
+
/** The task, built from the workflow input and the dependencies' outputs. */
|
|
252
|
+
prompt: (ctx: TypedStepContext<TInput, TDeps, AiContext<THost>>) => string;
|
|
253
|
+
tools?: (ctx: TypedStepContext<TInput, TDeps, AiContext<THost>>) => ToolSet;
|
|
254
|
+
maxRounds?: number;
|
|
255
|
+
maxTokens?: number;
|
|
256
|
+
maxOutputTokens?: number;
|
|
257
|
+
retry?: RetryPolicy;
|
|
258
|
+
timeoutMs?: number;
|
|
259
|
+
/** See `defineStep`'s `heartbeatTimeoutMs` — every round beats. */
|
|
260
|
+
heartbeatTimeoutMs?: number;
|
|
261
|
+
/** See `defineStep`'s `onFailure`. */
|
|
262
|
+
onFailure?: 'fail' | {
|
|
263
|
+
output: {
|
|
264
|
+
answer: TAnswer;
|
|
265
|
+
trace: AgentTrace;
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
resources?: readonly string[];
|
|
269
|
+
when?: (ctx: TypedStepContext<TInput, TDeps, AiContext<THost>>) => boolean | Promise<boolean>;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A step that runs one agent on the step's instrumented model. Its output is
|
|
273
|
+
* `{ answer, trace }`: dependents read `ctx.deps.<key>.answer`, and the trace
|
|
274
|
+
* stays on the run for whoever reads it back.
|
|
275
|
+
*/
|
|
276
|
+
export declare function defineAgentStep<TInput extends Record<string, unknown>, TAnswer extends Record<string, unknown>, THost = unknown, TDeps extends Record<string, TypedStep<any, any, any, AiContext<THost>>> = {}>(config: DefineAgentStepConfig<TInput, TAnswer, THost, TDeps>): TypedStep<TInput, {
|
|
277
|
+
answer: TAnswer;
|
|
278
|
+
trace: AgentTrace;
|
|
279
|
+
}, TDeps, AiContext<THost>>;
|
|
280
|
+
//#endregion
|
|
187
281
|
//#region src/ai/hooks.d.ts
|
|
188
282
|
export interface AiModelResolver<THost = unknown> {
|
|
189
283
|
/** Resolve the base (uninstrumented) model for a step. */
|
|
@@ -222,6 +316,32 @@ export interface AiUsageRecorder {
|
|
|
222
316
|
date: string;
|
|
223
317
|
}): Promise<void>;
|
|
224
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* What one step's model usage looks like to a metering sink: the booking
|
|
321
|
+
* `@octabits-io/metering`'s `record` takes, by shape — tokens as the vendor
|
|
322
|
+
* reported them, the provider as the service, and the attribution octaflow
|
|
323
|
+
* knows (the run, the workflow type as the feature, the step). A sink that
|
|
324
|
+
* runs inside `withUsage` adds the paying account from the ambient context.
|
|
325
|
+
*/
|
|
326
|
+
export interface AiModelUsageEvent {
|
|
327
|
+
kind: 'model';
|
|
328
|
+
/** The provider (`model.provider`), or `'model'` when the call did not report one. */
|
|
329
|
+
service: string;
|
|
330
|
+
model: string;
|
|
331
|
+
inputTokens: number;
|
|
332
|
+
outputTokens: number;
|
|
333
|
+
cacheReadTokens: number;
|
|
334
|
+
cacheWriteTokens: number;
|
|
335
|
+
context: {
|
|
336
|
+
runId: string;
|
|
337
|
+
feature: string;
|
|
338
|
+
correlationId?: string;
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/** Where a step's model usage is booked — `@octabits-io/metering`'s `Metering` satisfies it. Optional: without one nothing is booked. */
|
|
342
|
+
export interface AiUsageSink {
|
|
343
|
+
record(event: AiModelUsageEvent): unknown;
|
|
344
|
+
}
|
|
225
345
|
export interface AiQuotaPolicy {
|
|
226
346
|
/** Return an error (e.g. `ai_quota_exceeded`) to reject a workflow start. */
|
|
227
347
|
checkQuota(): Promise<Result<void, FlowErrorShape>>;
|
|
@@ -231,6 +351,12 @@ export interface CreateAiWorkflowHooksDeps<THost = unknown> {
|
|
|
231
351
|
usageRecorder: AiUsageRecorder;
|
|
232
352
|
/** Optional quota guard. BYOK exemption etc. is the policy's concern. */
|
|
233
353
|
quotaPolicy?: AiQuotaPolicy;
|
|
354
|
+
/**
|
|
355
|
+
* Optional metering sink: every step's model usage is booked here as well,
|
|
356
|
+
* unpriced, attributed to the run and the workflow type — so a model call
|
|
357
|
+
* and the paid API calls around it land in one table with one context.
|
|
358
|
+
*/
|
|
359
|
+
usage?: AiUsageSink;
|
|
234
360
|
/** Defaults to `createCostEstimator()` (the built-in pricing table). */
|
|
235
361
|
costEstimator?: CostEstimator;
|
|
236
362
|
/** Clock injection for tests. */
|
package/dist/ai/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as err, r as ok, t as noopLogger } from "../logger-epkdcXDA.js";
|
|
2
|
-
import { _ as defineStep, m as buildWorkflow } from "../core-DBqxBk3R.js";
|
|
2
|
+
import { C as nonRetryableError, _ as defineStep, m as buildWorkflow } from "../core-DBqxBk3R.js";
|
|
3
3
|
import { S as readRefusalDetails, _ as maskKeys, a as testProvider, b as createRefusalAwareModel, c as DEFAULT_PROVIDER_ERROR_DETAIL_LENGTH, d as PROVIDER_ERROR_REDACTED, f as PROVIDER_ERROR_STATUS, g as isProviderError, h as isFatalProviderError, i as testOllamaModel, l as FATAL_PROVIDER_ERROR_KEYS, m as explainProviderError, n as DEFAULT_PROVIDER_PROBE_PROMPT, o as createCircuitBreaker, p as ProviderError, r as DEFAULT_PROVIDER_PROBE_TIMEOUT_MS, s as withCircuitBreaker, t as DEFAULT_OLLAMA_PROBE_TIMEOUT_MS, u as PROVIDER_ERROR_PHRASES, v as AiRefusalError, x as isAiRefusalError, y as REFUSAL_FINISH_REASON } from "../provider-probe-CGN2MpaZ.js";
|
|
4
4
|
import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, i as DEFAULT_EMBEDDING_PROVIDER, l as embeddingDimensionsFor, n as AI_PROVIDER_IDS, o as EMBEDDING_PROVIDER_IDS, r as DEFAULT_AI_PROVIDER, s as OPENAI_COMPATIBLE_PROVIDER, t as AI_PROVIDERS, u as isSubprocessorResidency } from "../provider-registry-B91lgOmu.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { Output, generateText, wrapEmbeddingModel, wrapLanguageModel } from "ai";
|
|
6
|
+
import { Output, generateText, stepCountIs, tool, wrapEmbeddingModel, wrapLanguageModel } from "ai";
|
|
7
7
|
//#region src/ai/instrumented-model.ts
|
|
8
8
|
function createUsageAccumulator() {
|
|
9
9
|
const total = {
|
|
@@ -21,6 +21,7 @@ function createUsageAccumulator() {
|
|
|
21
21
|
total.cacheReadTokens += usage.cacheReadTokens;
|
|
22
22
|
total.cacheWriteTokens += usage.cacheWriteTokens;
|
|
23
23
|
if (usage.modelId) total.modelId = usage.modelId;
|
|
24
|
+
if (usage.provider) total.provider = usage.provider;
|
|
24
25
|
hasAny = true;
|
|
25
26
|
},
|
|
26
27
|
get() {
|
|
@@ -48,7 +49,8 @@ function createInstrumentedModel(model, accumulator) {
|
|
|
48
49
|
outputTokens: usage.outputTokens?.total ?? 0,
|
|
49
50
|
cacheReadTokens: usage.inputTokens?.cacheRead ?? 0,
|
|
50
51
|
cacheWriteTokens: usage.inputTokens?.cacheWrite ?? 0,
|
|
51
|
-
modelId: model.modelId
|
|
52
|
+
modelId: model.modelId,
|
|
53
|
+
provider: model.provider
|
|
52
54
|
});
|
|
53
55
|
return result;
|
|
54
56
|
},
|
|
@@ -63,7 +65,8 @@ function createInstrumentedModel(model, accumulator) {
|
|
|
63
65
|
outputTokens: u.outputTokens?.total ?? 0,
|
|
64
66
|
cacheReadTokens: u.inputTokens?.cacheRead ?? 0,
|
|
65
67
|
cacheWriteTokens: u.inputTokens?.cacheWrite ?? 0,
|
|
66
|
-
modelId: model.modelId
|
|
68
|
+
modelId: model.modelId,
|
|
69
|
+
provider: model.provider
|
|
67
70
|
});
|
|
68
71
|
}
|
|
69
72
|
} }));
|
|
@@ -409,6 +412,141 @@ function createModelDecider(options = {}) {
|
|
|
409
412
|
};
|
|
410
413
|
}
|
|
411
414
|
//#endregion
|
|
415
|
+
//#region src/ai/agent.ts
|
|
416
|
+
const DEFAULT_MAX_ROUNDS = 8;
|
|
417
|
+
function tokensOf(steps) {
|
|
418
|
+
return steps.reduce((sum, s) => sum + (s.usage.inputTokens ?? 0) + (s.usage.outputTokens ?? 0), 0);
|
|
419
|
+
}
|
|
420
|
+
/** The first exit called with valid arguments in a round — an invalid one is answered with its error and the loop goes on. */
|
|
421
|
+
function exitIn(step, exits) {
|
|
422
|
+
return step?.toolCalls.find((c) => !c.invalid && Object.hasOwn(exits, c.toolName));
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Run one agent until it takes one of `exits`, or fails. Stopping on a limit
|
|
426
|
+
* before an exit is not retryable — the same budget would stop the same way —
|
|
427
|
+
* so the step fails, or takes its `onFailure` stand-in.
|
|
428
|
+
*/
|
|
429
|
+
async function runAgentToExit(options) {
|
|
430
|
+
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
431
|
+
const { maxTokens, exits } = options;
|
|
432
|
+
for (const name of Object.keys(exits)) if (options.tools && Object.hasOwn(options.tools, name)) throw new Error(`agent tool "${name}" collides with an exit`);
|
|
433
|
+
const exitTools = Object.fromEntries(Object.entries(exits).map(([name, e]) => [name, tool({
|
|
434
|
+
description: e.description,
|
|
435
|
+
inputSchema: e.schema,
|
|
436
|
+
execute: async () => ({ accepted: true })
|
|
437
|
+
})]));
|
|
438
|
+
const stopWhen = [({ steps }) => exitIn(steps.at(-1), exits) !== void 0];
|
|
439
|
+
if (maxTokens !== void 0) stopWhen.push(({ steps }) => tokensOf(steps) >= maxTokens);
|
|
440
|
+
const abort = new AbortController();
|
|
441
|
+
const signal = options.abortSignal ? AbortSignal.any([options.abortSignal, abort.signal]) : abort.signal;
|
|
442
|
+
const exitList = Object.keys(exits).join(", ");
|
|
443
|
+
const reminder = (finish) => finish === "length" ? `Your reply was cut off at the output limit. Think less, act now: call a tool, or finish by calling one of: ${exitList}.` : `Continue with your tools. To finish, call one of: ${exitList}.`;
|
|
444
|
+
const steps = [];
|
|
445
|
+
const messages = [{
|
|
446
|
+
role: "user",
|
|
447
|
+
content: options.prompt
|
|
448
|
+
}];
|
|
449
|
+
let taken;
|
|
450
|
+
while (steps.length < maxRounds) {
|
|
451
|
+
const result = await generateText({
|
|
452
|
+
model: options.model,
|
|
453
|
+
system: options.system,
|
|
454
|
+
messages,
|
|
455
|
+
tools: {
|
|
456
|
+
...options.tools,
|
|
457
|
+
...exitTools
|
|
458
|
+
},
|
|
459
|
+
stopWhen: [stepCountIs(maxRounds - steps.length), ...stopWhen],
|
|
460
|
+
maxOutputTokens: options.maxOutputTokens,
|
|
461
|
+
maxRetries: options.maxRetries ?? 0,
|
|
462
|
+
abortSignal: signal,
|
|
463
|
+
onStepFinish: options.heartbeat ? async () => {
|
|
464
|
+
if (!await options.heartbeat()) abort.abort();
|
|
465
|
+
} : void 0
|
|
466
|
+
});
|
|
467
|
+
steps.push(...result.steps);
|
|
468
|
+
taken = exitIn(steps.at(-1), exits);
|
|
469
|
+
const budgetLeft = maxTokens === void 0 || tokensOf(steps) < maxTokens;
|
|
470
|
+
if (taken || result.finishReason === "tool-calls" || !budgetLeft) break;
|
|
471
|
+
messages.push(...result.responseMessages, {
|
|
472
|
+
role: "user",
|
|
473
|
+
content: reminder(result.finishReason)
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const trace = {
|
|
477
|
+
rounds: steps.length,
|
|
478
|
+
toolCalls: steps.flatMap((s) => s.toolCalls.map((c) => c.toolName)),
|
|
479
|
+
tokens: tokensOf(steps)
|
|
480
|
+
};
|
|
481
|
+
if (!taken) throw nonRetryableError(`agent stopped without taking an exit after ${trace.rounds} rounds and ${trace.tokens} tokens (limits: ${maxRounds} rounds${maxTokens !== void 0 ? `, ${maxTokens} tokens` : ""})`);
|
|
482
|
+
return {
|
|
483
|
+
exit: taken.toolName,
|
|
484
|
+
value: taken.input,
|
|
485
|
+
trace
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
/** Run one agent to a typed answer: `runAgentToExit` with the single exit `answer`. */
|
|
489
|
+
async function runAgent(options) {
|
|
490
|
+
const { output, ...rest } = options;
|
|
491
|
+
const run = await runAgentToExit({
|
|
492
|
+
...rest,
|
|
493
|
+
exits: { answer: {
|
|
494
|
+
description: "Give your final answer. Call this once you are done.",
|
|
495
|
+
schema: output
|
|
496
|
+
} }
|
|
497
|
+
});
|
|
498
|
+
return {
|
|
499
|
+
output: run.value,
|
|
500
|
+
trace: run.trace
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/** The trace as a schema — for a step output that carries one. */
|
|
504
|
+
const AGENT_TRACE_SCHEMA = z.object({
|
|
505
|
+
rounds: z.number(),
|
|
506
|
+
toolCalls: z.array(z.string()),
|
|
507
|
+
tokens: z.number()
|
|
508
|
+
});
|
|
509
|
+
/**
|
|
510
|
+
* A step that runs one agent on the step's instrumented model. Its output is
|
|
511
|
+
* `{ answer, trace }`: dependents read `ctx.deps.<key>.answer`, and the trace
|
|
512
|
+
* stays on the run for whoever reads it back.
|
|
513
|
+
*/
|
|
514
|
+
function defineAgentStep(config) {
|
|
515
|
+
return defineStep({
|
|
516
|
+
type: config.type,
|
|
517
|
+
workflowInputSchema: config.workflowInputSchema,
|
|
518
|
+
outputSchema: z.object({
|
|
519
|
+
answer: config.answerSchema,
|
|
520
|
+
trace: AGENT_TRACE_SCHEMA
|
|
521
|
+
}),
|
|
522
|
+
dependencies: config.dependencies,
|
|
523
|
+
retry: config.retry,
|
|
524
|
+
timeoutMs: config.timeoutMs,
|
|
525
|
+
heartbeatTimeoutMs: config.heartbeatTimeoutMs,
|
|
526
|
+
onFailure: config.onFailure,
|
|
527
|
+
resources: config.resources,
|
|
528
|
+
when: config.when,
|
|
529
|
+
handler: async (ctx) => {
|
|
530
|
+
const { output, trace } = await runAgent({
|
|
531
|
+
model: ctx.context.model,
|
|
532
|
+
system: config.system,
|
|
533
|
+
prompt: config.prompt(ctx),
|
|
534
|
+
tools: config.tools?.(ctx),
|
|
535
|
+
output: config.answerSchema,
|
|
536
|
+
maxRounds: config.maxRounds,
|
|
537
|
+
maxTokens: config.maxTokens,
|
|
538
|
+
maxOutputTokens: config.maxOutputTokens,
|
|
539
|
+
heartbeat: ctx.heartbeat,
|
|
540
|
+
abortSignal: ctx.signal
|
|
541
|
+
});
|
|
542
|
+
return {
|
|
543
|
+
answer: output,
|
|
544
|
+
trace
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
//#endregion
|
|
412
550
|
//#region src/ai/hooks.ts
|
|
413
551
|
/**
|
|
414
552
|
* Wire the AI behaviors (model instrumentation, token/cost capture, quota,
|
|
@@ -440,7 +578,7 @@ function createAiWorkflowHooks(deps) {
|
|
|
440
578
|
usage
|
|
441
579
|
};
|
|
442
580
|
},
|
|
443
|
-
async onAfterStep({ workflowId, stepId, context }) {
|
|
581
|
+
async onAfterStep({ workflowId, stepId, workflow, step, context }) {
|
|
444
582
|
const acc = context.usage;
|
|
445
583
|
if (!acc.hasUsage()) return;
|
|
446
584
|
const usage = acc.get();
|
|
@@ -456,6 +594,19 @@ function createAiWorkflowHooks(deps) {
|
|
|
456
594
|
usage,
|
|
457
595
|
costMicros
|
|
458
596
|
});
|
|
597
|
+
deps.usage?.record({
|
|
598
|
+
kind: "model",
|
|
599
|
+
service: usage.provider ?? "model",
|
|
600
|
+
model: usage.modelId,
|
|
601
|
+
inputTokens: usage.inputTokens,
|
|
602
|
+
outputTokens: usage.outputTokens,
|
|
603
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
604
|
+
cacheWriteTokens: usage.cacheWriteTokens,
|
|
605
|
+
context: {
|
|
606
|
+
runId: String(workflowId),
|
|
607
|
+
feature: workflow?.type ?? step?.key ?? "workflow"
|
|
608
|
+
}
|
|
609
|
+
});
|
|
459
610
|
},
|
|
460
611
|
async onWorkflowCompleted({ workflowId, workflow }) {
|
|
461
612
|
if (!deps.usageRecorder.recordWorkflowDaily) return;
|
|
@@ -1134,4 +1285,4 @@ function isRecord(value) {
|
|
|
1134
1285
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1135
1286
|
}
|
|
1136
1287
|
//#endregion
|
|
1137
|
-
export { AI_PROVIDERS, AI_PROVIDER_IDS, AiRefusalError, DEFAULT_AI_PROVIDER, DEFAULT_AI_QUOTA, DEFAULT_EMBEDDING_PROVIDER, DEFAULT_MODEL_JSON_EXCERPT_LENGTH, DEFAULT_MODEL_PRICING, DEFAULT_OLLAMA_PROBE_TIMEOUT_MS, DEFAULT_PROVIDER_ERROR_DETAIL_LENGTH, DEFAULT_PROVIDER_PROBE_PROMPT, DEFAULT_PROVIDER_PROBE_TIMEOUT_MS, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, FATAL_PROVIDER_ERROR_KEYS, MODEL_JSON_PROVIDER_METADATA_KEY, MODEL_JSON_REPAIRED_ATTRIBUTE, ModelJsonError, OPENAI_COMPATIBLE_PROVIDER, OPENAI_COMPATIBLE_PROVIDER_ID, PROVIDER_ERROR_PHRASES, PROVIDER_ERROR_REDACTED, PROVIDER_ERROR_STATUS, ProviderError, REFUSAL_FINISH_REASON, ZERO_MODEL_PRICING, addUsage, anthropicThinkingSettings, buildAiWorkflow, createAiQuotaService, createAiUsageAggregationService, createAiWorkflowHooks, createCircuitBreaker, createCostEstimator, createEmbeddingUsageAccumulator, createInstrumentedEmbeddingModel, createInstrumentedModel, createModelDecider, createRefusalAwareModel, createUsageAccumulator, defineAiStep, embeddingDimensionsFor, estimateCostMicros, excerptOf, explainProviderError, isAiRefusalError, isFatalProviderError, isModelJsonError, isProviderError, isSubprocessorResidency, maskKeys, mergeModelJsonObjects, modelJsonRepairText, parseModelJson, readRefusalDetails, recoverModelJson, resolveEndpointResidency, supportsAdaptiveThinking, supportsToolSearch, testOllamaModel, testProvider, thinkingSettingsFor, withCircuitBreaker, withJsonRepair };
|
|
1288
|
+
export { AGENT_TRACE_SCHEMA, AI_PROVIDERS, AI_PROVIDER_IDS, AiRefusalError, DEFAULT_AI_PROVIDER, DEFAULT_AI_QUOTA, DEFAULT_EMBEDDING_PROVIDER, DEFAULT_MODEL_JSON_EXCERPT_LENGTH, DEFAULT_MODEL_PRICING, DEFAULT_OLLAMA_PROBE_TIMEOUT_MS, DEFAULT_PROVIDER_ERROR_DETAIL_LENGTH, DEFAULT_PROVIDER_PROBE_PROMPT, DEFAULT_PROVIDER_PROBE_TIMEOUT_MS, EMBEDDING_PROVIDERS, EMBEDDING_PROVIDER_IDS, FATAL_PROVIDER_ERROR_KEYS, MODEL_JSON_PROVIDER_METADATA_KEY, MODEL_JSON_REPAIRED_ATTRIBUTE, ModelJsonError, OPENAI_COMPATIBLE_PROVIDER, OPENAI_COMPATIBLE_PROVIDER_ID, PROVIDER_ERROR_PHRASES, PROVIDER_ERROR_REDACTED, PROVIDER_ERROR_STATUS, ProviderError, REFUSAL_FINISH_REASON, ZERO_MODEL_PRICING, addUsage, anthropicThinkingSettings, buildAiWorkflow, createAiQuotaService, createAiUsageAggregationService, createAiWorkflowHooks, createCircuitBreaker, createCostEstimator, createEmbeddingUsageAccumulator, createInstrumentedEmbeddingModel, createInstrumentedModel, createModelDecider, createRefusalAwareModel, createUsageAccumulator, defineAgentStep, defineAiStep, embeddingDimensionsFor, estimateCostMicros, excerptOf, explainProviderError, isAiRefusalError, isFatalProviderError, isModelJsonError, isProviderError, isSubprocessorResidency, maskKeys, mergeModelJsonObjects, modelJsonRepairText, parseModelJson, readRefusalDetails, recoverModelJson, resolveEndpointResidency, runAgent, runAgentToExit, supportsAdaptiveThinking, supportsToolSearch, testOllamaModel, testProvider, thinkingSettingsFor, withCircuitBreaker, withJsonRepair };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { b as OpenAiCompatibleProviderId, g as EmbeddingProviderId, i as AiEndpointResidency, o as AiProviderId } from "../../provider-registry-C_mOfdHH.js";
|
|
2
|
-
import { A as maskKeys, C as ProviderErrorExplanation, D as explainProviderError, E as ProviderErrorPreset, O as isFatalProviderError, S as ProviderError, T as ProviderErrorPhrase, _ as ExplainProviderErrorOptions, a as OllamaProbeReport, c as ProviderProbeOptions, d as testProvider, f as CircuitBreaker, h as withCircuitBreaker, i as OllamaProbeOptions, k as isProviderError, l as ProviderProbeReport, m as createCircuitBreaker, o as ProbeableProvider, p as CircuitBreakerOptions, s as ProviderProbeFailure, u as testOllamaModel, v as FATAL_PROVIDER_ERROR_KEYS, w as ProviderErrorKey, x as PROVIDER_ERROR_STATUS, y as PROVIDER_ERROR_PHRASES } from "../../provider-probe-
|
|
2
|
+
import { A as maskKeys, C as ProviderErrorExplanation, D as explainProviderError, E as ProviderErrorPreset, O as isFatalProviderError, S as ProviderError, T as ProviderErrorPhrase, _ as ExplainProviderErrorOptions, a as OllamaProbeReport, c as ProviderProbeOptions, d as testProvider, f as CircuitBreaker, h as withCircuitBreaker, i as OllamaProbeOptions, k as isProviderError, l as ProviderProbeReport, m as createCircuitBreaker, o as ProbeableProvider, p as CircuitBreakerOptions, s as ProviderProbeFailure, u as testOllamaModel, v as FATAL_PROVIDER_ERROR_KEYS, w as ProviderErrorKey, x as PROVIDER_ERROR_STATUS, y as PROVIDER_ERROR_PHRASES } from "../../provider-probe-_rio4ykx.js";
|
|
3
3
|
import { EmbeddingModelV4, LanguageModelV4 } from "@ai-sdk/provider";
|
|
4
4
|
//#region src/ai/providers/index.d.ts
|
|
5
5
|
export type LanguageModelFactory = (modelId: string) => LanguageModelV4;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { St as Result, lt as WorkflowId, nt as StepId, xt as FlowErrorShape } from "./index-D0XhoTkE.js";
|
|
2
2
|
//#region src/core/dispatcher.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* What a step job is for. A queue carries two kinds of work for a step, and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { St as Result, W as StartOptions, ct as WorkflowDefinition, ft as WorkflowRecord, lt as WorkflowId, nt as StepId, rt as StepRecord, xt as FlowErrorShape } from "./index-D0XhoTkE.js";
|
|
2
2
|
//#region src/core/hooks.d.ts
|
|
3
3
|
interface BeforeStartArgs {
|
|
4
4
|
definition: WorkflowDefinition;
|
|
@@ -61,6 +61,8 @@ declare function computeReadiness(steps: StepRecord[], joinOf: (stepType: string
|
|
|
61
61
|
type WorkflowId = number;
|
|
62
62
|
type StepId = number;
|
|
63
63
|
type WorkflowStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
64
|
+
/** A durable start delay in ms — fixed, or computed from the run's input when the step becomes ready. */
|
|
65
|
+
type StepDelay = number | ((workflowInput: Record<string, unknown>) => number);
|
|
64
66
|
type StepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'waiting' | 'mapping' | 'compensating' | 'compensated';
|
|
65
67
|
/**
|
|
66
68
|
* Declarative workflow definition: a DAG of steps with dependencies.
|
|
@@ -278,9 +280,11 @@ interface StepRegistration<TContext = unknown> {
|
|
|
278
280
|
/**
|
|
279
281
|
* Durable start delay in ms: once the step becomes ready (all deps complete), its
|
|
280
282
|
* first dispatch is held for this long via the queue. A no-op handler with a delay
|
|
281
|
-
* is a durable "sleep" step. Does not affect retry backoff.
|
|
283
|
+
* is a durable "sleep" step. Does not affect retry backoff. A function computes
|
|
284
|
+
* it from the run's input when the step becomes ready — a wait whose length the
|
|
285
|
+
* run decided (an agent's "check again in 3 days").
|
|
282
286
|
*/
|
|
283
|
-
delayMs?:
|
|
287
|
+
delayMs?: StepDelay;
|
|
284
288
|
/**
|
|
285
289
|
* When true, a ready step **suspends** (status `waiting`) instead of being dispatched,
|
|
286
290
|
* until `engine.resumeStep(workflowId, stepKey, payload)` delivers an external event.
|
|
@@ -326,7 +330,7 @@ interface StepHandlerRegistry<TContext = unknown> {
|
|
|
326
330
|
resources?: readonly string[];
|
|
327
331
|
heartbeatTimeoutMs?: number;
|
|
328
332
|
heartbeat?: 'auto' | 'manual';
|
|
329
|
-
delayMs?:
|
|
333
|
+
delayMs?: StepDelay;
|
|
330
334
|
waitForEvent?: boolean;
|
|
331
335
|
map?: boolean;
|
|
332
336
|
childType?: string;
|
|
@@ -451,8 +455,8 @@ interface TypedStep<TInput extends Record<string, unknown> = Record<string, unkn
|
|
|
451
455
|
readonly heartbeatTimeoutMs?: number;
|
|
452
456
|
/** Who beats: the engine on a timer (`'auto'`, default) or only `ctx.heartbeat()`. */
|
|
453
457
|
readonly heartbeat?: 'auto' | 'manual';
|
|
454
|
-
/** Optional durable start delay in ms (held in the queue once the step is ready). */
|
|
455
|
-
readonly delayMs?:
|
|
458
|
+
/** Optional durable start delay in ms (held in the queue once the step is ready), fixed or from the run's input. */
|
|
459
|
+
readonly delayMs?: StepDelay;
|
|
456
460
|
/** When true, the step suspends (waiting) until `engine.resumeStep` delivers an event. */
|
|
457
461
|
readonly waitForEvent?: boolean;
|
|
458
462
|
/** Untyped guard built from the typed `when` config — `false` skips the step. */
|
|
@@ -610,8 +614,11 @@ interface DefineStepConfig<TInput extends Record<string, unknown>, TOutput exten
|
|
|
610
614
|
* silence also means a *hung* handler.
|
|
611
615
|
*/
|
|
612
616
|
heartbeat?: 'auto' | 'manual';
|
|
613
|
-
/**
|
|
614
|
-
|
|
617
|
+
/**
|
|
618
|
+
* Optional durable start delay in ms — held in the queue once the step is
|
|
619
|
+
* ready. A function computes it from the run's input at that moment.
|
|
620
|
+
*/
|
|
621
|
+
delayMs?: number | ((workflowInput: TInput) => number);
|
|
615
622
|
/** When true, the step suspends (waiting) until `engine.resumeStep` delivers an event. */
|
|
616
623
|
waitForEvent?: boolean;
|
|
617
624
|
/**
|
|
@@ -769,8 +776,8 @@ declare function defineSubWorkflowStep<TOutput extends Record<string, unknown> =
|
|
|
769
776
|
*/
|
|
770
777
|
declare function defineSleepStep<TContext = unknown, TDeps extends Record<string, TypedStep<any, any, any, TContext>> = {}>(config: {
|
|
771
778
|
type: string;
|
|
772
|
-
/** How long to sleep once the step becomes ready, in ms. */
|
|
773
|
-
sleepMs:
|
|
779
|
+
/** How long to sleep once the step becomes ready, in ms — fixed, or computed from the run's input then. */
|
|
780
|
+
sleepMs: StepDelay;
|
|
774
781
|
dependencies?: TDeps;
|
|
775
782
|
}): TypedStep<Record<string, never>, Record<string, never>, TDeps, TContext>;
|
|
776
783
|
/** Minimal engine surface `buildWorkflow().start()` needs — avoids a circular import. */
|
|
@@ -1098,4 +1105,4 @@ declare function toPublicStep(step: StepRecord): PublicWorkflowStep;
|
|
|
1098
1105
|
*/
|
|
1099
1106
|
declare function toPublicWorkflow(workflow: WorkflowWithSteps): PublicWorkflow;
|
|
1100
1107
|
//#endregion
|
|
1101
|
-
export {
|
|
1108
|
+
export { StepHandler as $, StepOutput as A, defineWaitStep as B, defineDecisionOutcomeStep as C, err as Ct, resolveDecision as D, functionDecider as E, buildWorkflow as F, StepCompensateHandler as G, InvalidWorkflowDefinitionError as H, defineMapStep as I, StepDefinition as J, StepCompensationContext as K, defineSleepStep as L, TypedStepContext as M, TypedWorkflow as N, DefineStepConfig as O, WorkflowOutput as P, StepFailurePolicy as Q, defineStep as R, DecisionStep as S, Result as St, defineDecisionStep as T, RetryPolicy as U, FlowError as V, StartOptions as W, StepError as X, StepDelay as Y, StepExecutionContext as Z, DecisionOutput as _, PlannedSkip as _t, STEP_DISPLAY_STATUS as a, StepStatus as at, DecisionResponse as b, isTerminalStepStatus as bt, WORKFLOW_STATUS_SCHEMA as c, WorkflowDefinition as ct, toPublicWorkflow as d, WorkflowNotRetryableError as dt, StepHandlerNotFoundError as et, Decider as f, WorkflowRecord as ft, DecisionOutcome as g, JoinRule as gt, DecisionChoices as h, WorkflowWithSteps as ht, PublicWorkflowStep as i, StepRegistration as it, TypedStep as j, StepDeps as k, toDisplayStepStatus as l, WorkflowId as lt, DecisionAnswers as m, WorkflowStepRunningError as mt, PUBLIC_WORKFLOW_STEP_SCHEMA as n, StepId as nt, STEP_DISPLAY_STATUS_SCHEMA as o, WaitTimeoutPolicy as ot, DecisionAnswer as p, WorkflowStatus as pt, StepConditionHandler as q, PublicWorkflow as r, StepRecord as rt, StepDisplayStatus as s, WorkflowCreatedResult as st, PUBLIC_WORKFLOW_SCHEMA as t, StepHandlerRegistry as tt, toPublicStep as u, WorkflowNotFoundError as ut, DecisionQuestions as v, ReadinessPlan as vt, defineDecisionReviewStep as w, ok as wt, DecisionReview as x, FlowErrorShape as xt, DecisionRequest as y, computeReadiness as yt, defineSubWorkflowStep as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as StepHandler, A as StepOutput, B as defineWaitStep, C as defineDecisionOutcomeStep, Ct as err, D as resolveDecision, E as functionDecider, F as buildWorkflow, G as StepCompensateHandler, H as InvalidWorkflowDefinitionError, I as defineMapStep, J as StepDefinition, K as StepCompensationContext, L as defineSleepStep, M as TypedStepContext, N as TypedWorkflow, O as DefineStepConfig, P as WorkflowOutput, Q as StepFailurePolicy, R as defineStep, S as DecisionStep, St as Result, T as defineDecisionStep, U as RetryPolicy, V as FlowError, W as StartOptions, X as StepError, Y as StepDelay, Z as StepExecutionContext, _ as DecisionOutput, _t as PlannedSkip, a as STEP_DISPLAY_STATUS, at as StepStatus, b as DecisionResponse, bt as isTerminalStepStatus, c as WORKFLOW_STATUS_SCHEMA, ct as WorkflowDefinition, d as toPublicWorkflow, dt as WorkflowNotRetryableError, et as StepHandlerNotFoundError, f as Decider, ft as WorkflowRecord, g as DecisionOutcome, gt as JoinRule, h as DecisionChoices, ht as WorkflowWithSteps, i as PublicWorkflowStep, it as StepRegistration, j as TypedStep, k as StepDeps, l as toDisplayStepStatus, lt as WorkflowId, m as DecisionAnswers, mt as WorkflowStepRunningError, n as PUBLIC_WORKFLOW_STEP_SCHEMA, nt as StepId, o as STEP_DISPLAY_STATUS_SCHEMA, ot as WaitTimeoutPolicy, p as DecisionAnswer, pt as WorkflowStatus, q as StepConditionHandler, r as PublicWorkflow, rt as StepRecord, s as StepDisplayStatus, st as WorkflowCreatedResult, t as PUBLIC_WORKFLOW_SCHEMA, tt as StepHandlerRegistry, u as toPublicStep, ut as WorkflowNotFoundError, v as DecisionQuestions, vt as ReadinessPlan, w as defineDecisionReviewStep, wt as ok, x as DecisionReview, xt as FlowErrorShape, y as DecisionRequest, yt as computeReadiness, z as defineSubWorkflowStep } from "./index-D0XhoTkE.js";
|
|
2
2
|
import { n as Logger, r as noopLogger, t as LogAttributes } from "./logger-BCus3AOa.js";
|
|
3
|
-
import { A as FinishWorkflowParams, C as gateRuleKeys, D as CreateWorkflowStep, E as CreateWorkflowParams, F as WorkflowTreeFilter, M as ReopenWorkflowParams, N as TransactionalScope, O as CreatedWorkflow, P as WorkflowStore, S as createInMemoryStepGate, T as CompleteStepParams, _ as InMemoryStepGateConfig, a as FlowSpan, b as StepGateDecision, c as RecordingObserver, d as createMeterObserver, f as createRecordingObserver, g as ConcurrencyRule, h as noopTracer, i as FlowObserver, j as ListWorkflowsFilters, k as FailStepParams, l as RecordingTracer, m as noopObserver, n as FlowEventType, o as FlowTracer, p as createRecordingTracer, r as FlowMeter, s as RecordedSpan, t as FlowEvent, u as combineObservers, v as RateRule, w as AddChildStep, x as StepGateRequest, y as StepGate } from "./observability-
|
|
4
|
-
import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-
|
|
5
|
-
import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-
|
|
3
|
+
import { A as FinishWorkflowParams, C as gateRuleKeys, D as CreateWorkflowStep, E as CreateWorkflowParams, F as WorkflowTreeFilter, M as ReopenWorkflowParams, N as TransactionalScope, O as CreatedWorkflow, P as WorkflowStore, S as createInMemoryStepGate, T as CompleteStepParams, _ as InMemoryStepGateConfig, a as FlowSpan, b as StepGateDecision, c as RecordingObserver, d as createMeterObserver, f as createRecordingObserver, g as ConcurrencyRule, h as noopTracer, i as FlowObserver, j as ListWorkflowsFilters, k as FailStepParams, l as RecordingTracer, m as noopObserver, n as FlowEventType, o as FlowTracer, p as createRecordingTracer, r as FlowMeter, s as RecordedSpan, t as FlowEvent, u as combineObservers, v as RateRule, w as AddChildStep, x as StepGateRequest, y as StepGate } from "./observability-0MLzBjuV.js";
|
|
4
|
+
import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-B3ruILCN.js";
|
|
5
|
+
import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-C46TPBEg.js";
|
|
6
6
|
//#region src/core/registry.d.ts
|
|
7
7
|
/**
|
|
8
8
|
* Creates an in-memory step handler registry.
|
|
@@ -168,4 +168,4 @@ export declare function createInMemoryWorkflowStore(partitionKey?: string): Work
|
|
|
168
168
|
};
|
|
169
169
|
};
|
|
170
170
|
//#endregion
|
|
171
|
-
export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, Decider, DecisionAnswer, DecisionAnswers, DecisionChoices, DecisionOutcome, DecisionOutput, DecisionQuestions, DecisionRequest, DecisionResponse, DecisionReview, DecisionStep, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowMeter, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepFailurePolicy, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStepRunningError, WorkflowStore, WorkflowTreeFilter, WorkflowWithSteps, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createMeterObserver, createRecordingObserver, createRecordingTracer, defineDecisionOutcomeStep, defineDecisionReviewStep, defineDecisionStep, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, functionDecider, gateRuleKeys, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, resolveDecision, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
|
|
171
|
+
export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, Decider, DecisionAnswer, DecisionAnswers, DecisionChoices, DecisionOutcome, DecisionOutput, DecisionQuestions, DecisionRequest, DecisionResponse, DecisionReview, DecisionStep, DefineStepConfig, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowMeter, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDelay, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepFailurePolicy, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStepRunningError, WorkflowStore, WorkflowTreeFilter, WorkflowWithSteps, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createMeterObserver, createRecordingObserver, createRecordingTracer, defineDecisionOutcomeStep, defineDecisionReviewStep, defineDecisionStep, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, functionDecider, gateRuleKeys, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, resolveDecision, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
|
package/dist/index.js
CHANGED
|
@@ -436,10 +436,18 @@ function createWorkflowEngine(deps) {
|
|
|
436
436
|
}
|
|
437
437
|
};
|
|
438
438
|
}
|
|
439
|
-
/**
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
/**
|
|
440
|
+
* Durable start delay for a ready step, from its registered `delayMs`. A
|
|
441
|
+
* function reads the run's input — the one extra read, and only for such steps.
|
|
442
|
+
*/
|
|
443
|
+
async function readyStepDelay(stepType, workflowId, st = store) {
|
|
444
|
+
const delay = registry.getRegistration(stepType)?.delayMs;
|
|
445
|
+
let delayMs = typeof delay === "number" ? delay : void 0;
|
|
446
|
+
if (typeof delay === "function") {
|
|
447
|
+
const workflow = await st.getWorkflow(workflowId);
|
|
448
|
+
delayMs = workflow ? delay(workflow.input ?? {}) : void 0;
|
|
449
|
+
}
|
|
450
|
+
if (!delayMs || !Number.isFinite(delayMs) || delayMs <= 0) return void 0;
|
|
443
451
|
return { startAfterSeconds: Math.ceil(delayMs / 1e3) };
|
|
444
452
|
}
|
|
445
453
|
/** Whether a step type suspends on readiness instead of dispatching. */
|
|
@@ -531,7 +539,7 @@ function createWorkflowEngine(deps) {
|
|
|
531
539
|
stepKey,
|
|
532
540
|
stepType
|
|
533
541
|
};
|
|
534
|
-
const delay = readyStepDelay(stepType);
|
|
542
|
+
const delay = await readyStepDelay(stepType, workflowId, st);
|
|
535
543
|
const result = scope ? await dispatcher.enqueueStepIn(scope.handle, payload, delay) : await dispatcher.enqueueStep(payload, delay);
|
|
536
544
|
if (!result.ok) {
|
|
537
545
|
logger.error("Failed to enqueue step", new Error(result.error.message), {
|
|
@@ -1289,7 +1297,7 @@ function createWorkflowEngine(deps) {
|
|
|
1289
1297
|
stepId: child.id,
|
|
1290
1298
|
stepKey: child.key,
|
|
1291
1299
|
stepType: child.type
|
|
1292
|
-
}, readyStepDelay(child.type));
|
|
1300
|
+
}, await readyStepDelay(child.type, workflowId));
|
|
1293
1301
|
if (!result.ok) logger.error("Failed to enqueue map child", new Error(result.error.message), {
|
|
1294
1302
|
workflowId,
|
|
1295
1303
|
stepKey: child.key
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ft as WorkflowRecord, ht as WorkflowWithSteps, lt as WorkflowId, nt as StepId, pt as WorkflowStatus, rt as StepRecord } from "./index-D0XhoTkE.js";
|
|
2
2
|
//#region src/core/store.d.ts
|
|
3
3
|
interface CreateWorkflowStep {
|
|
4
4
|
key: string;
|
package/dist/pg-boss/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "../index-
|
|
1
|
+
import "../index-D0XhoTkE.js";
|
|
2
2
|
import { n as Logger } from "../logger-BCus3AOa.js";
|
|
3
|
-
import { r as Dispatcher } from "../dispatcher-
|
|
3
|
+
import { r as Dispatcher } from "../dispatcher-B3ruILCN.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { PgBoss, Schedule } from "pg-boss";
|
|
6
6
|
//#region src/pg-boss/payload.d.ts
|
package/dist/postgres/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { P as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-
|
|
1
|
+
import { lt as WorkflowId } from "../index-D0XhoTkE.js";
|
|
2
|
+
import { P as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-0MLzBjuV.js";
|
|
3
3
|
import { Pool } from "pg";
|
|
4
4
|
//#region src/postgres/ddl.d.ts
|
|
5
5
|
/**
|
|
@@ -42,6 +42,53 @@ export interface SqlExecutor {
|
|
|
42
42
|
* batteries-included executor used by the `createPg*` adapters.
|
|
43
43
|
*/
|
|
44
44
|
export declare function poolExecutor(pool: Pool): SqlExecutor;
|
|
45
|
+
/**
|
|
46
|
+
* The slice of a postgres.js `sql` instance {@link postgresJsExecutor} uses —
|
|
47
|
+
* structural, so the driver is never imported. `sql.unsafe` resolves to the row
|
|
48
|
+
* list with the affected-row `count` on it; `sql.begin` runs its callback on one
|
|
49
|
+
* reserved connection inside `BEGIN`/`COMMIT` and hands it a `sql` of the same shape.
|
|
50
|
+
*/
|
|
51
|
+
export interface PostgresJsSqlLike {
|
|
52
|
+
unsafe(query: string, params?: unknown[]): PromiseLike<unknown[] & {
|
|
53
|
+
count?: number | null;
|
|
54
|
+
}>;
|
|
55
|
+
begin<T>(fn: (tx: PostgresJsSqlLike) => Promise<T>): PromiseLike<T>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build a {@link SqlExecutor} over a postgres.js `sql` instance: top-level
|
|
59
|
+
* queries run on its pool (autocommit); `transaction` runs `fn` inside
|
|
60
|
+
* `sql.begin`, so every query of `fn` shares one connection and a throw rolls
|
|
61
|
+
* back. For a host whose app already speaks postgres.js (Drizzle's
|
|
62
|
+
* `drizzle-orm/postgres-js`) and that would otherwise open a `pg` pool just for
|
|
63
|
+
* the workflow store.
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* import postgres from 'postgres';
|
|
67
|
+
* const sql = postgres(process.env.DATABASE_URL);
|
|
68
|
+
* const store = createWorkflowStore({ exec: postgresJsExecutor(sql), partitionKey });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* Wrap the result to set row-level-security GUCs first, exactly as with
|
|
72
|
+
* {@link poolExecutor}.
|
|
73
|
+
*/
|
|
74
|
+
export declare function postgresJsExecutor(sql: PostgresJsSqlLike): SqlExecutor;
|
|
75
|
+
/** The slice of a PGlite instance {@link pgliteExecutor} uses — structural, so PGlite is never imported. */
|
|
76
|
+
export interface PgliteLike {
|
|
77
|
+
query<R>(query: string, params?: unknown[]): Promise<{
|
|
78
|
+
rows: R[];
|
|
79
|
+
affectedRows?: number;
|
|
80
|
+
}>;
|
|
81
|
+
}
|
|
82
|
+
/** A PGlite instance: {@link PgliteLike} plus its `transaction`. */
|
|
83
|
+
export interface PgliteDatabaseLike extends PgliteLike {
|
|
84
|
+
transaction<T>(fn: (tx: PgliteLike) => Promise<T>): Promise<T>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build a {@link SqlExecutor} over a PGlite instance — the embedded Postgres a
|
|
88
|
+
* host runs its tests (or a single-process deployment) on. `transaction` uses
|
|
89
|
+
* PGlite's own, which serialises with every other query on the instance.
|
|
90
|
+
*/
|
|
91
|
+
export declare function pgliteExecutor(db: PgliteDatabaseLike): SqlExecutor;
|
|
45
92
|
/**
|
|
46
93
|
* Normalize a `Pool | SqlExecutor` argument to a {@link SqlExecutor}. Lets the
|
|
47
94
|
* read helpers accept either the batteries-included pool or a host's RLS-scoped
|
package/dist/postgres/index.js
CHANGED
|
@@ -126,6 +126,58 @@ function poolExecutor(pool) {
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
|
+
* Build a {@link SqlExecutor} over a postgres.js `sql` instance: top-level
|
|
130
|
+
* queries run on its pool (autocommit); `transaction` runs `fn` inside
|
|
131
|
+
* `sql.begin`, so every query of `fn` shares one connection and a throw rolls
|
|
132
|
+
* back. For a host whose app already speaks postgres.js (Drizzle's
|
|
133
|
+
* `drizzle-orm/postgres-js`) and that would otherwise open a `pg` pool just for
|
|
134
|
+
* the workflow store.
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* import postgres from 'postgres';
|
|
138
|
+
* const sql = postgres(process.env.DATABASE_URL);
|
|
139
|
+
* const store = createWorkflowStore({ exec: postgresJsExecutor(sql), partitionKey });
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* Wrap the result to set row-level-security GUCs first, exactly as with
|
|
143
|
+
* {@link poolExecutor}.
|
|
144
|
+
*/
|
|
145
|
+
function postgresJsExecutor(sql) {
|
|
146
|
+
const over = (conn, inTransaction) => ({
|
|
147
|
+
async query(text, params) {
|
|
148
|
+
const rows = await conn.unsafe(text, params ?? []);
|
|
149
|
+
return {
|
|
150
|
+
rows: Array.from(rows),
|
|
151
|
+
rowCount: rows.count ?? rows.length
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
transaction(fn) {
|
|
155
|
+
return inTransaction ? fn(over(conn, true)) : Promise.resolve(conn.begin((tx) => fn(over(tx, true))));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
return over(sql, false);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Build a {@link SqlExecutor} over a PGlite instance — the embedded Postgres a
|
|
162
|
+
* host runs its tests (or a single-process deployment) on. `transaction` uses
|
|
163
|
+
* PGlite's own, which serialises with every other query on the instance.
|
|
164
|
+
*/
|
|
165
|
+
function pgliteExecutor(db) {
|
|
166
|
+
const over = (conn, tx) => ({
|
|
167
|
+
async query(text, params) {
|
|
168
|
+
const res = await conn.query(text, params ?? []);
|
|
169
|
+
return {
|
|
170
|
+
rows: res.rows,
|
|
171
|
+
rowCount: res.affectedRows ?? res.rows.length
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
transaction(fn) {
|
|
175
|
+
return tx ? fn(over(conn, true)) : db.transaction((t) => fn(over(t, true)));
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return over(db, false);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
129
181
|
* Normalize a `Pool | SqlExecutor` argument to a {@link SqlExecutor}. Lets the
|
|
130
182
|
* read helpers accept either the batteries-included pool or a host's RLS-scoped
|
|
131
183
|
* executor without a breaking signature change. `SqlExecutor` is discriminated by
|
|
@@ -794,4 +846,4 @@ async function readFlowEvents(db, params) {
|
|
|
794
846
|
}));
|
|
795
847
|
}
|
|
796
848
|
//#endregion
|
|
797
|
-
export { FLOW_EVENT_DDL, FLOW_GATE_DDL, FLOW_STORE_DDL, applySchema, createEventSink, createPgEventSink, createPgStepGate, createPgWorkflowStore, createSchemaDdl, createStepGate, createWorkflowStore, flowEventDdl, flowGateDdl, flowStoreDdl, poolExecutor, readFlowEvents, toExecutor };
|
|
849
|
+
export { FLOW_EVENT_DDL, FLOW_GATE_DDL, FLOW_STORE_DDL, applySchema, createEventSink, createPgEventSink, createPgStepGate, createPgWorkflowStore, createSchemaDdl, createStepGate, createWorkflowStore, flowEventDdl, flowGateDdl, flowStoreDdl, pgliteExecutor, poolExecutor, postgresJsExecutor, readFlowEvents, toExecutor };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "octaflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Durable DAG workflow engine: Zod-typed steps over Postgres + pg-boss, with an optional AI add-on (token/cost/quota). Layered single package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -122,6 +122,7 @@
|
|
|
122
122
|
"@ai-sdk/openai": "^4.0.95",
|
|
123
123
|
"@ai-sdk/openai-compatible": "^3.0.70",
|
|
124
124
|
"@ai-sdk/provider": "^4.0.28",
|
|
125
|
+
"@electric-sql/pglite": "0.5.10",
|
|
125
126
|
"@testcontainers/postgresql": "^12.2.1",
|
|
126
127
|
"@types/node": "^26.6.5",
|
|
127
128
|
"@types/pg": "^8.23.1",
|