octaflow 0.25.0 → 0.26.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
CHANGED
|
@@ -295,6 +295,10 @@ export const flowStepLease = pgTable('flow_step_lease', flowStepLeaseColumns, (t
|
|
|
295
295
|
]);
|
|
296
296
|
```
|
|
297
297
|
|
|
298
|
+
The names are yours: the store's `ON CONFLICT` clauses target column lists, never a
|
|
299
|
+
constraint by name, so what must match is each index's columns and its partial `WHERE`,
|
|
300
|
+
not what you call it.
|
|
301
|
+
|
|
298
302
|
The store stays plain SQL, bound to the `schema` you hand `createPgWorkflowStore`,
|
|
299
303
|
`createPgStepGate` and `createPgEventSink` — and, for the gate, to the `tables` names — so
|
|
300
304
|
declare the tables under the same schema and names. A test in the package holds every column
|
|
@@ -66,7 +66,7 @@ export interface LazyEmbeddingModelMeta {
|
|
|
66
66
|
* next call rather than cached.
|
|
67
67
|
*/
|
|
68
68
|
export declare function createLazyEmbeddingModel(meta: LazyEmbeddingModelMeta, load: () => Promise<EmbeddingModelV4>): EmbeddingModelV4;
|
|
69
|
-
export type AiProviderConfigErrorKey = 'ai_residency_undeclared' | 'ai_residency_invalid' | 'ai_base_url_invalid' | 'ai_model_required' | 'ai_embedding_dimensions_invalid';
|
|
69
|
+
export type AiProviderConfigErrorKey = 'ai_residency_undeclared' | 'ai_residency_invalid' | 'ai_base_url_invalid' | 'ai_model_required' | 'ai_model_undeclared' | 'ai_embedding_dimensions_invalid';
|
|
70
70
|
/**
|
|
71
71
|
* A provider configuration that cannot work. Thrown before any SDK is loaded
|
|
72
72
|
* or any request is made: it is a deployment mistake, found at wiring time.
|
|
@@ -143,4 +143,66 @@ export interface OpenAiCompatibleEmbeddingProvider {
|
|
|
143
143
|
export declare function createOpenAiCompatibleModelProvider(config: OpenAiCompatibleModelProviderConfig): Promise<OpenAiCompatibleModelProvider>;
|
|
144
144
|
/** An embedding model on an OpenAI-compatible endpoint, with its declared width. */
|
|
145
145
|
export declare function createOpenAiCompatibleEmbeddingProvider(config: OpenAiCompatibleEmbeddingProviderConfig): Promise<OpenAiCompatibleEmbeddingProvider>;
|
|
146
|
+
export interface OpenAiCompatibleGatewayModel {
|
|
147
|
+
/** Where the gateway sends this alias. Validated like an endpoint's residency. */
|
|
148
|
+
residency: AiEndpointResidency;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* `residency` is where the gateway itself runs, usually `{ region: 'self-hosted' }`.
|
|
152
|
+
* It decides whether plain http is acceptable for the hop to the gateway; where a
|
|
153
|
+
* call ends up is the alias's residency in `models`.
|
|
154
|
+
*/
|
|
155
|
+
export interface OpenAiCompatibleGatewayConfig extends OpenAiCompatibleEndpointConfig {
|
|
156
|
+
/** Every alias this deployment may call, keyed by the model name the gateway knows. */
|
|
157
|
+
models: Record<string, OpenAiCompatibleGatewayModel>;
|
|
158
|
+
/** As for {@link OpenAiCompatibleModelProviderConfig.structuredOutputs}. */
|
|
159
|
+
structuredOutputs?: boolean;
|
|
160
|
+
/** As for {@link OpenAiCompatibleModelProviderConfig.includeUsage}. */
|
|
161
|
+
includeUsage?: boolean;
|
|
162
|
+
}
|
|
163
|
+
export interface GatewayCallOptions {
|
|
164
|
+
/**
|
|
165
|
+
* Sent as the request body's `metadata`, which LiteLLM keeps in its spend log,
|
|
166
|
+
* so a call there can be matched to the host's own record: a workflow id, a
|
|
167
|
+
* scope key. It leaves the house as written and is never redacted: put ids
|
|
168
|
+
* here, never content. Added innermost, so a disclosure gate wrapped around the
|
|
169
|
+
* model neither digests nor redacts it.
|
|
170
|
+
*/
|
|
171
|
+
metadata?: Record<string, string>;
|
|
172
|
+
}
|
|
173
|
+
export interface OpenAiCompatibleGatewayProvider {
|
|
174
|
+
providerId: OpenAiCompatibleProviderId;
|
|
175
|
+
/** Where the gateway itself runs. */
|
|
176
|
+
residency: AiEndpointResidency;
|
|
177
|
+
/** Every declared alias with its validated residency: what a settings page or a subprocessor annex lists. */
|
|
178
|
+
models: Readonly<Record<string, AiEndpointResidency>>;
|
|
179
|
+
/** The residency of one alias. Throws `ai_model_undeclared` for an alias the declaration does not name. */
|
|
180
|
+
residencyOf(alias: string): AiEndpointResidency;
|
|
181
|
+
/** A Chat Completions model for a declared alias, wrapped for refusal detection. */
|
|
182
|
+
languageModel(alias: string, options?: GatewayCallOptions): LanguageModelV4;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* A model gateway — LiteLLM, or any router with the OpenAI-compatible API — as
|
|
186
|
+
* refusal-aware models, one per declared alias, each with the residency of
|
|
187
|
+
* where the gateway sends it.
|
|
188
|
+
*
|
|
189
|
+
* ```ts
|
|
190
|
+
* const gateway = await createOpenAiCompatibleGatewayProvider({
|
|
191
|
+
* baseUrl: 'http://litellm.ai.svc:4000/v1',
|
|
192
|
+
* apiKey: process.env.LITELLM_KEY,
|
|
193
|
+
* residency: { region: 'self-hosted' },
|
|
194
|
+
* models: {
|
|
195
|
+
* 'approval-confidential': { residency: { region: 'self-hosted' } },
|
|
196
|
+
* 'approval-public': { residency: { region: 'eu', legalName: 'Example Inference GmbH', country: 'DE' } },
|
|
197
|
+
* },
|
|
198
|
+
* });
|
|
199
|
+
* const model = gateway.languageModel('approval-confidential', { metadata: { workflowId } });
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* The declaration is what the gateway is configured to do, stated by the
|
|
203
|
+
* deployment: this provider cannot see the gateway's routing, and a fallback
|
|
204
|
+
* the gateway adds on its own breaks the declaration silently. Keep the two
|
|
205
|
+
* in the same review.
|
|
206
|
+
*/
|
|
207
|
+
export declare function createOpenAiCompatibleGatewayProvider(config: OpenAiCompatibleGatewayConfig): Promise<OpenAiCompatibleGatewayProvider>;
|
|
146
208
|
//#endregion
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { r as createRefusalAwareModel } from "../../refusal-BsL2HPDb.js";
|
|
2
2
|
import { a as EMBEDDING_PROVIDERS, c as OPENAI_COMPATIBLE_PROVIDER_ID, d as resolveEndpointResidency, t as AI_PROVIDERS } from "../../provider-registry-BRwjC6_3.js";
|
|
3
|
+
import { wrapLanguageModel } from "ai";
|
|
3
4
|
//#region src/ai/providers/index.ts
|
|
4
5
|
const LLM_LOADERS = {
|
|
5
6
|
anthropic: async (apiKey, baseURL) => {
|
|
@@ -186,5 +187,90 @@ async function createOpenAiCompatibleEmbeddingProvider(config) {
|
|
|
186
187
|
embeddingModel: () => provider.embeddingModel(modelId)
|
|
187
188
|
};
|
|
188
189
|
}
|
|
190
|
+
/** Puts `metadata` into the request body under the provider's own options key, merged over any the call carries. */
|
|
191
|
+
function withGatewayMetadata(model, optionsKey, metadata) {
|
|
192
|
+
const fixed = { ...metadata };
|
|
193
|
+
return wrapLanguageModel({
|
|
194
|
+
model,
|
|
195
|
+
middleware: {
|
|
196
|
+
specificationVersion: "v4",
|
|
197
|
+
transformParams: async ({ params }) => {
|
|
198
|
+
const own = params.providerOptions?.[optionsKey] ?? {};
|
|
199
|
+
const existing = own.metadata !== null && typeof own.metadata === "object" && !Array.isArray(own.metadata) ? own.metadata : {};
|
|
200
|
+
const merged = {
|
|
201
|
+
...own,
|
|
202
|
+
metadata: {
|
|
203
|
+
...existing,
|
|
204
|
+
...fixed
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
...params,
|
|
209
|
+
providerOptions: {
|
|
210
|
+
...params.providerOptions,
|
|
211
|
+
[optionsKey]: merged
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A model gateway — LiteLLM, or any router with the OpenAI-compatible API — as
|
|
220
|
+
* refusal-aware models, one per declared alias, each with the residency of
|
|
221
|
+
* where the gateway sends it.
|
|
222
|
+
*
|
|
223
|
+
* ```ts
|
|
224
|
+
* const gateway = await createOpenAiCompatibleGatewayProvider({
|
|
225
|
+
* baseUrl: 'http://litellm.ai.svc:4000/v1',
|
|
226
|
+
* apiKey: process.env.LITELLM_KEY,
|
|
227
|
+
* residency: { region: 'self-hosted' },
|
|
228
|
+
* models: {
|
|
229
|
+
* 'approval-confidential': { residency: { region: 'self-hosted' } },
|
|
230
|
+
* 'approval-public': { residency: { region: 'eu', legalName: 'Example Inference GmbH', country: 'DE' } },
|
|
231
|
+
* },
|
|
232
|
+
* });
|
|
233
|
+
* const model = gateway.languageModel('approval-confidential', { metadata: { workflowId } });
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* The declaration is what the gateway is configured to do, stated by the
|
|
237
|
+
* deployment: this provider cannot see the gateway's routing, and a fallback
|
|
238
|
+
* the gateway adds on its own breaks the declaration silently. Keep the two
|
|
239
|
+
* in the same review.
|
|
240
|
+
*/
|
|
241
|
+
async function createOpenAiCompatibleGatewayProvider(config) {
|
|
242
|
+
const residency = checkEndpoint(config);
|
|
243
|
+
const declared = config.models;
|
|
244
|
+
if (declared === null || typeof declared !== "object" || Object.keys(declared).length === 0) throw new AiProviderConfigError("ai_model_required", "a gateway needs at least one declared model alias in models");
|
|
245
|
+
const models = {};
|
|
246
|
+
for (const [alias, model] of Object.entries(declared)) {
|
|
247
|
+
requireModel(alias, "a model alias");
|
|
248
|
+
const resolved = resolveEndpointResidency(model?.residency);
|
|
249
|
+
if (!resolved.ok) throw new AiProviderConfigError(resolved.error.key, `model ${JSON.stringify(alias)}: ${resolved.error.message}`);
|
|
250
|
+
models[alias] = resolved.value;
|
|
251
|
+
}
|
|
252
|
+
Object.freeze(models);
|
|
253
|
+
const provider = await loadOpenAiCompatible(config, {
|
|
254
|
+
structuredOutputs: config.structuredOutputs,
|
|
255
|
+
includeUsage: config.includeUsage
|
|
256
|
+
});
|
|
257
|
+
const optionsKey = config.name ?? "openai-compatible";
|
|
258
|
+
const residencyOf = (alias) => {
|
|
259
|
+
const found = Object.hasOwn(models, alias) ? models[alias] : void 0;
|
|
260
|
+
if (found === void 0) throw new AiProviderConfigError("ai_model_undeclared", `model ${JSON.stringify(alias)} is not declared for this gateway; declared: ${Object.keys(models).join(", ")}`);
|
|
261
|
+
return found;
|
|
262
|
+
};
|
|
263
|
+
return {
|
|
264
|
+
providerId: OPENAI_COMPATIBLE_PROVIDER_ID,
|
|
265
|
+
residency,
|
|
266
|
+
models,
|
|
267
|
+
residencyOf,
|
|
268
|
+
languageModel: (alias, options) => {
|
|
269
|
+
residencyOf(alias);
|
|
270
|
+
const chat = provider.chatModel(alias);
|
|
271
|
+
return createRefusalAwareModel(options?.metadata ? withGatewayMetadata(chat, optionsKey, options.metadata) : chat);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
189
275
|
//#endregion
|
|
190
|
-
export { AiProviderConfigError, createEmbeddingModelFactory, createEmbeddingModelProvider, createLanguageModelFactory, createLanguageModelProvider, createLazyEmbeddingModel, createOpenAiCompatibleEmbeddingProvider, createOpenAiCompatibleModelProvider };
|
|
276
|
+
export { AiProviderConfigError, createEmbeddingModelFactory, createEmbeddingModelProvider, createLanguageModelFactory, createLanguageModelProvider, createLazyEmbeddingModel, createOpenAiCompatibleEmbeddingProvider, createOpenAiCompatibleGatewayProvider, createOpenAiCompatibleModelProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $ as computeReadiness, A as StepConditionHandler, B as StepStatus, C as defineWaitStep, D as StartOptions, E as RetryPolicy, F as StepHandlerNotFoundError, G as WorkflowNotFoundError, H as WorkflowCreatedResult, I as StepHandlerRegistry, J as WorkflowStatus, K as WorkflowNotRetryableError, L as StepId, M as StepError, N as StepExecutionContext, O as StepCompensateHandler, P as StepHandler, Q as ReadinessPlan, R as StepRecord, S as defineSubWorkflowStep, T as InvalidWorkflowDefinitionError, U as WorkflowDefinition, V as WaitTimeoutPolicy, W as WorkflowId, X as JoinRule, Y as WorkflowWithSteps, Z as PlannedSkip, _ as WorkflowOutput, a as STEP_DISPLAY_STATUS, b as defineSleepStep, c as WORKFLOW_STATUS_SCHEMA, d as toPublicWorkflow, et as isTerminalStepStatus, f as StepDeps, g as TypedWorkflow, h as TypedStepContext, i as PublicWorkflowStep, it as ok, j as StepDefinition, k as StepCompensationContext, l as toDisplayStepStatus, m as TypedStep, n as PUBLIC_WORKFLOW_STEP_SCHEMA, nt as Result, o as STEP_DISPLAY_STATUS_SCHEMA, p as StepOutput, q as WorkflowRecord, r as PublicWorkflow, rt as err, s as StepDisplayStatus, t as PUBLIC_WORKFLOW_SCHEMA, tt as FlowErrorShape, u as toPublicStep, v as buildWorkflow, w as FlowError, x as defineStep, y as defineMapStep, z as StepRegistration } from "./index-BURAYZui.js";
|
|
2
2
|
import { n as Logger, r as noopLogger, t as LogAttributes } from "./logger-BCus3AOa.js";
|
|
3
|
-
import { A as ListWorkflowsFilters, C as AddChildStep, D as CreatedWorkflow, E as CreateWorkflowStep, M as TransactionalScope, N as WorkflowStore, O as FailStepParams, S as createInMemoryStepGate, T as CreateWorkflowParams, _ 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 ReopenWorkflowParams, k as FinishWorkflowParams, 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 CompleteStepParams, x as StepGateRequest, y as StepGate } from "./observability-
|
|
3
|
+
import { A as ListWorkflowsFilters, C as AddChildStep, D as CreatedWorkflow, E as CreateWorkflowStep, M as TransactionalScope, N as WorkflowStore, O as FailStepParams, S as createInMemoryStepGate, T as CreateWorkflowParams, _ 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 ReopenWorkflowParams, k as FinishWorkflowParams, 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 CompleteStepParams, x as StepGateRequest, y as StepGate } from "./observability-3wVqt3tZ.js";
|
|
4
4
|
import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-g7Ct0J6w.js";
|
|
5
5
|
import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-BdxIcc92.js";
|
|
6
6
|
//#region src/core/registry.d.ts
|
|
@@ -320,7 +320,7 @@ declare function createRecordingObserver(): RecordingObserver;
|
|
|
320
320
|
* The slice of a metrics API the engine's meter observer needs. Structural:
|
|
321
321
|
* `@octabits-io/telemetry`'s meter and an OpenTelemetry `Meter`
|
|
322
322
|
* both fit (the SDK's `createCounter`/`createHistogram` need a two-line
|
|
323
|
-
* adapter
|
|
323
|
+
* adapter).
|
|
324
324
|
*/
|
|
325
325
|
interface FlowMeter {
|
|
326
326
|
counter(name: string, options?: {
|
package/dist/postgres/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { W as WorkflowId } from "../index-BURAYZui.js";
|
|
2
|
-
import { N as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-
|
|
2
|
+
import { N as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-3wVqt3tZ.js";
|
|
3
3
|
import { Pool } from "pg";
|
|
4
4
|
//#region src/postgres/ddl.d.ts
|
|
5
5
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "octaflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.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",
|
|
@@ -116,23 +116,23 @@
|
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"devDependencies": {
|
|
119
|
-
"@ai-sdk/anthropic": "^4.0.
|
|
120
|
-
"@ai-sdk/mistral": "^4.0.
|
|
121
|
-
"@ai-sdk/openai": "^4.0.
|
|
122
|
-
"@ai-sdk/openai-compatible": "^3.0.
|
|
123
|
-
"@ai-sdk/provider": "^4.0.
|
|
124
|
-
"@testcontainers/postgresql": "^12.
|
|
125
|
-
"@types/node": "^26.
|
|
119
|
+
"@ai-sdk/anthropic": "^4.0.69",
|
|
120
|
+
"@ai-sdk/mistral": "^4.0.55",
|
|
121
|
+
"@ai-sdk/openai": "^4.0.82",
|
|
122
|
+
"@ai-sdk/openai-compatible": "^3.0.60",
|
|
123
|
+
"@ai-sdk/provider": "^4.0.20",
|
|
124
|
+
"@testcontainers/postgresql": "^12.2.0",
|
|
125
|
+
"@types/node": "^26.6.3",
|
|
126
126
|
"@types/pg": "^8.23.1",
|
|
127
|
-
"ai": "^7.0.
|
|
128
|
-
"drizzle-orm": "^0.45.
|
|
129
|
-
"pg": "^8.23.
|
|
130
|
-
"pg-boss": "^12.
|
|
131
|
-
"testcontainers": "^12.
|
|
127
|
+
"ai": "^7.0.123",
|
|
128
|
+
"drizzle-orm": "^0.45.3",
|
|
129
|
+
"pg": "^8.23.1",
|
|
130
|
+
"pg-boss": "^12.35.0",
|
|
131
|
+
"testcontainers": "^12.2.0",
|
|
132
132
|
"tsdown": "^0.23.0",
|
|
133
133
|
"typescript": "^7.0.2",
|
|
134
|
-
"vitest": "^5.0.
|
|
135
|
-
"zod": "^4.6.
|
|
134
|
+
"vitest": "^5.0.3",
|
|
135
|
+
"zod": "^4.6.5"
|
|
136
136
|
},
|
|
137
137
|
"scripts": {
|
|
138
138
|
"build": "tsdown",
|