@db-lyon/flowkit 0.10.0 → 0.11.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 +37 -0
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +377 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +60 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/flow/runner.d.ts +14 -1
- package/dist/flow/runner.d.ts.map +1 -1
- package/dist/flow/runner.js +72 -2
- package/dist/flow/runner.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/task/agent-prompt-task.d.ts +26 -7
- package/dist/task/agent-prompt-task.d.ts.map +1 -1
- package/dist/task/agent-prompt-task.js +53 -19
- package/dist/task/agent-prompt-task.js.map +1 -1
- package/dist/task/agent-task.d.ts +98 -0
- package/dist/task/agent-task.d.ts.map +1 -0
- package/dist/task/agent-task.js +254 -0
- package/dist/task/agent-task.js.map +1 -0
- package/dist/task/base-task.d.ts +25 -0
- package/dist/task/base-task.d.ts.map +1 -1
- package/dist/task/base-task.js.map +1 -1
- package/dist/task/concurrency.d.ts +9 -0
- package/dist/task/concurrency.d.ts.map +1 -0
- package/dist/task/concurrency.js +24 -0
- package/dist/task/concurrency.js.map +1 -0
- package/dist/task/index.d.ts +11 -1
- package/dist/task/index.d.ts.map +1 -1
- package/dist/task/index.js +6 -0
- package/dist/task/index.js.map +1 -1
- package/dist/task/json-schema.d.ts +37 -0
- package/dist/task/json-schema.d.ts.map +1 -0
- package/dist/task/json-schema.js +223 -0
- package/dist/task/json-schema.js.map +1 -0
- package/dist/task/llm-provider.d.ts +81 -8
- package/dist/task/llm-provider.d.ts.map +1 -1
- package/dist/task/llm-provider.js +9 -3
- package/dist/task/llm-provider.js.map +1 -1
- package/dist/task/llm-runner.d.ts +72 -0
- package/dist/task/llm-runner.d.ts.map +1 -0
- package/dist/task/llm-runner.js +213 -0
- package/dist/task/llm-runner.js.map +1 -0
- package/dist/task/redact.d.ts +22 -0
- package/dist/task/redact.d.ts.map +1 -0
- package/dist/task/redact.js +47 -0
- package/dist/task/redact.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single place every LLM call goes through.
|
|
3
|
+
*
|
|
4
|
+
* `runCompletion` wraps a raw provider with the cross-cutting concerns that make
|
|
5
|
+
* model calls production-safe — and does so once, so both the single-shot
|
|
6
|
+
* `AgentPromptTask` and the agentic `AgentTask` inherit identical behavior:
|
|
7
|
+
*
|
|
8
|
+
* - timeout + abort — bound every call; abort the provider's in-flight request
|
|
9
|
+
* - retry + backoff — exponential backoff on transient transport failures
|
|
10
|
+
* - structured output — validate against the requested JSON Schema and, on a
|
|
11
|
+
* mismatch, re-prompt the model with the concrete errors
|
|
12
|
+
* (the "repair loop") before giving up
|
|
13
|
+
* - output cap — bound response text so a runaway generation can't blow
|
|
14
|
+
* up memory or downstream logs
|
|
15
|
+
*/
|
|
16
|
+
import { noopLogger } from '../logger.js';
|
|
17
|
+
import { validateJson, formatErrors } from './json-schema.js';
|
|
18
|
+
/** Lift the run-control fields out of a task's options into `LLMRunOptions`. */
|
|
19
|
+
export function pickRunOptions(o) {
|
|
20
|
+
const out = {};
|
|
21
|
+
if (o.timeout !== undefined)
|
|
22
|
+
out.timeout = o.timeout;
|
|
23
|
+
if (o.retries !== undefined)
|
|
24
|
+
out.retries = o.retries;
|
|
25
|
+
if (o.retryDelay !== undefined)
|
|
26
|
+
out.retryDelay = o.retryDelay;
|
|
27
|
+
if (o.repairAttempts !== undefined)
|
|
28
|
+
out.repairAttempts = o.repairAttempts;
|
|
29
|
+
if (o.maxOutputChars !== undefined)
|
|
30
|
+
out.maxOutputChars = o.maxOutputChars;
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
/** A provider call exceeded its timeout. */
|
|
34
|
+
export class LLMTimeoutError extends Error {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'LLMTimeoutError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Structured output never satisfied the schema, even after repair attempts. */
|
|
41
|
+
export class StructuredOutputError extends Error {
|
|
42
|
+
rawText;
|
|
43
|
+
validationErrors;
|
|
44
|
+
constructor(message, rawText, validationErrors) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.rawText = rawText;
|
|
47
|
+
this.validationErrors = validationErrors;
|
|
48
|
+
this.name = 'StructuredOutputError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function runCompletion(provider, request, options = {}, logger = noopLogger) {
|
|
52
|
+
const { timeout = 60_000, retries = 2, retryDelay = 500, retryOn, repairAttempts = 1, maxOutputChars = 0, } = options;
|
|
53
|
+
// The first call goes out exactly as the caller framed it (a bare `prompt`
|
|
54
|
+
// stays a `prompt`). Only when a repair turn must be appended do we fall back
|
|
55
|
+
// to a `messages` conversation, seeded from the original prompt/messages.
|
|
56
|
+
let currentReq = request;
|
|
57
|
+
let repairsLeft = request.schema ? repairAttempts : 0;
|
|
58
|
+
let history = request.messages
|
|
59
|
+
? [...request.messages]
|
|
60
|
+
: request.prompt != null
|
|
61
|
+
? [{ role: 'user', content: request.prompt }]
|
|
62
|
+
: [];
|
|
63
|
+
// eslint-disable-next-line no-constant-condition
|
|
64
|
+
while (true) {
|
|
65
|
+
const raw = await callWithRetry(provider, currentReq, { timeout, retries, retryDelay, retryOn }, logger);
|
|
66
|
+
const resp = capOutput(raw, maxOutputChars, logger);
|
|
67
|
+
if (!request.schema)
|
|
68
|
+
return resp;
|
|
69
|
+
const coerced = coerceStructured(resp, request.schema);
|
|
70
|
+
if (!coerced.errors)
|
|
71
|
+
return { ...resp, parsed: coerced.parsed };
|
|
72
|
+
if (repairsLeft <= 0) {
|
|
73
|
+
throw new StructuredOutputError(`LLM output failed schema validation: ${coerced.errors}`, resp.text, coerced.errors);
|
|
74
|
+
}
|
|
75
|
+
repairsLeft--;
|
|
76
|
+
logger.warn({ errors: coerced.errors, repairsLeft }, 'LLM structured output failed validation; requesting repair');
|
|
77
|
+
history = [
|
|
78
|
+
...history,
|
|
79
|
+
{ role: 'assistant', content: resp.text },
|
|
80
|
+
{ role: 'user', content: repairInstruction(coerced.errors) },
|
|
81
|
+
];
|
|
82
|
+
currentReq = { ...request, prompt: undefined, messages: history };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Transport: timeout + retry/backoff
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
async function callWithRetry(provider, req, cfg, logger) {
|
|
89
|
+
let lastErr = new Error('LLM call never executed');
|
|
90
|
+
for (let attempt = 0; attempt <= cfg.retries; attempt++) {
|
|
91
|
+
try {
|
|
92
|
+
return await callOnce(provider, req, cfg.timeout);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
96
|
+
const canRetry = attempt < cfg.retries && (cfg.retryOn ? cfg.retryOn(lastErr) : true);
|
|
97
|
+
if (!canRetry)
|
|
98
|
+
break;
|
|
99
|
+
const delay = cfg.retryDelay * 2 ** attempt;
|
|
100
|
+
logger.warn({ attempt: attempt + 1, nextDelayMs: delay, error: lastErr.message }, 'LLM call failed; retrying');
|
|
101
|
+
await sleep(delay);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
throw lastErr;
|
|
105
|
+
}
|
|
106
|
+
async function callOnce(provider, req, timeout) {
|
|
107
|
+
if (!timeout || timeout <= 0)
|
|
108
|
+
return provider.complete(req);
|
|
109
|
+
const controller = new AbortController();
|
|
110
|
+
const signal = req.signal ? anySignal([req.signal, controller.signal]) : controller.signal;
|
|
111
|
+
let timer;
|
|
112
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
113
|
+
timer = setTimeout(() => {
|
|
114
|
+
controller.abort();
|
|
115
|
+
reject(new LLMTimeoutError(`LLM call timed out after ${timeout}ms`));
|
|
116
|
+
}, timeout);
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
return await Promise.race([provider.complete({ ...req, signal }), timeoutPromise]);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
if (timer)
|
|
123
|
+
clearTimeout(timer);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Combine abort signals — aborts when any input aborts. (Node-version safe.) */
|
|
127
|
+
function anySignal(signals) {
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const onAbort = () => {
|
|
130
|
+
controller.abort();
|
|
131
|
+
for (const s of signals)
|
|
132
|
+
s.removeEventListener('abort', onAbort);
|
|
133
|
+
};
|
|
134
|
+
for (const s of signals) {
|
|
135
|
+
if (s.aborted) {
|
|
136
|
+
controller.abort();
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
s.addEventListener('abort', onAbort, { once: true });
|
|
140
|
+
}
|
|
141
|
+
return controller.signal;
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Structured output
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
/**
|
|
147
|
+
* Coerce a response into a schema-conforming value: prefer the provider's
|
|
148
|
+
* `parsed`, else parse JSON out of the text. Returns `{ parsed }` when valid or
|
|
149
|
+
* `{ errors }` describing the mismatch. Exported so callers (e.g. the agent
|
|
150
|
+
* loop) can check conformance without forcing an extra model call.
|
|
151
|
+
*/
|
|
152
|
+
export function coerceStructured(resp, schema) {
|
|
153
|
+
let candidate = resp.parsed;
|
|
154
|
+
if (candidate === undefined) {
|
|
155
|
+
const parsed = extractJson(resp.text);
|
|
156
|
+
if (!parsed.ok)
|
|
157
|
+
return { errors: `output is not valid JSON (${parsed.error})` };
|
|
158
|
+
candidate = parsed.value;
|
|
159
|
+
}
|
|
160
|
+
const result = validateJson(candidate, schema);
|
|
161
|
+
if (!result.valid)
|
|
162
|
+
return { errors: formatErrors(result.errors) };
|
|
163
|
+
return { parsed: candidate };
|
|
164
|
+
}
|
|
165
|
+
/** Parse JSON from model output, tolerating code fences and surrounding prose. */
|
|
166
|
+
function extractJson(text) {
|
|
167
|
+
const tryParse = (s) => {
|
|
168
|
+
try {
|
|
169
|
+
return { ok: true, value: JSON.parse(s) };
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const trimmed = text.trim();
|
|
176
|
+
const direct = tryParse(trimmed);
|
|
177
|
+
if (direct)
|
|
178
|
+
return direct;
|
|
179
|
+
// Strip a ```json … ``` (or plain ```) fence if present.
|
|
180
|
+
const fence = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
181
|
+
if (fence?.[1]) {
|
|
182
|
+
const fenced = tryParse(fence[1].trim());
|
|
183
|
+
if (fenced)
|
|
184
|
+
return fenced;
|
|
185
|
+
}
|
|
186
|
+
// Last resort: slice from the first opening bracket to the last closing one.
|
|
187
|
+
const start = trimmed.search(/[[{]/);
|
|
188
|
+
const end = Math.max(trimmed.lastIndexOf('}'), trimmed.lastIndexOf(']'));
|
|
189
|
+
if (start !== -1 && end > start) {
|
|
190
|
+
const sliced = tryParse(trimmed.slice(start, end + 1));
|
|
191
|
+
if (sliced)
|
|
192
|
+
return sliced;
|
|
193
|
+
}
|
|
194
|
+
return { ok: false, error: 'no parseable JSON found' };
|
|
195
|
+
}
|
|
196
|
+
function repairInstruction(errors) {
|
|
197
|
+
return (`Your previous response did not satisfy the required JSON schema. ` +
|
|
198
|
+
`Validation errors: ${errors}. ` +
|
|
199
|
+
`Respond again with ONLY valid JSON that satisfies the schema — no prose, no markdown fences.`);
|
|
200
|
+
}
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Output cap
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
function capOutput(resp, maxChars, logger) {
|
|
205
|
+
if (!maxChars || maxChars <= 0 || resp.text.length <= maxChars)
|
|
206
|
+
return resp;
|
|
207
|
+
logger.warn({ length: resp.text.length, maxChars }, 'LLM output exceeded maxOutputChars; truncating');
|
|
208
|
+
return { ...resp, text: resp.text.slice(0, maxChars), truncated: true };
|
|
209
|
+
}
|
|
210
|
+
function sleep(ms) {
|
|
211
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=llm-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-runner.js","sourceRoot":"","sources":["../../src/task/llm-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging hygiene for the LLM tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provider config and prompts routinely carry API keys, tokens, and large or
|
|
5
|
+
* sensitive payloads. These helpers keep secrets out of logs and keep log lines
|
|
6
|
+
* bounded, so attaching a real logger to an agent task is safe by default.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Deep-clone `value`, masking the values of any keys that look secret. Strings
|
|
10
|
+
* longer than `maxString` are truncated with a length marker. Cycles are
|
|
11
|
+
* collapsed to `[circular]`. Never throws.
|
|
12
|
+
*/
|
|
13
|
+
export declare function redact(value: unknown, maxString?: number, seen?: WeakSet<object>): unknown;
|
|
14
|
+
/** Truncate a string to `max` chars, appending a `(+N more)` marker. */
|
|
15
|
+
export declare function truncate(str: string, max?: number): string;
|
|
16
|
+
/**
|
|
17
|
+
* A short, log-safe preview of model-bound text: collapses whitespace and
|
|
18
|
+
* truncates. Use for prompts/outputs you want visible at info level without
|
|
19
|
+
* dumping the whole payload.
|
|
20
|
+
*/
|
|
21
|
+
export declare function preview(str: string, max?: number): string;
|
|
22
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/task/redact.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,SAAM,EAAE,IAAI,kBAAwB,GAAG,OAAO,CAgB7F;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAGvD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAGtD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging hygiene for the LLM tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provider config and prompts routinely carry API keys, tokens, and large or
|
|
5
|
+
* sensitive payloads. These helpers keep secrets out of logs and keep log lines
|
|
6
|
+
* bounded, so attaching a real logger to an agent task is safe by default.
|
|
7
|
+
*/
|
|
8
|
+
const SECRET_KEY = /(?:api[-_]?key|secret|token|password|passwd|authorization|auth|bearer|credential|private[-_]?key)/i;
|
|
9
|
+
const REDACTED = '[redacted]';
|
|
10
|
+
/**
|
|
11
|
+
* Deep-clone `value`, masking the values of any keys that look secret. Strings
|
|
12
|
+
* longer than `maxString` are truncated with a length marker. Cycles are
|
|
13
|
+
* collapsed to `[circular]`. Never throws.
|
|
14
|
+
*/
|
|
15
|
+
export function redact(value, maxString = 500, seen = new WeakSet()) {
|
|
16
|
+
if (typeof value === 'string')
|
|
17
|
+
return truncate(value, maxString);
|
|
18
|
+
if (value === null || typeof value !== 'object')
|
|
19
|
+
return value;
|
|
20
|
+
if (seen.has(value))
|
|
21
|
+
return '[circular]';
|
|
22
|
+
seen.add(value);
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map((v) => redact(v, maxString, seen));
|
|
25
|
+
}
|
|
26
|
+
const out = {};
|
|
27
|
+
for (const [key, v] of Object.entries(value)) {
|
|
28
|
+
out[key] = SECRET_KEY.test(key) ? REDACTED : redact(v, maxString, seen);
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** Truncate a string to `max` chars, appending a `(+N more)` marker. */
|
|
33
|
+
export function truncate(str, max = 500) {
|
|
34
|
+
if (str.length <= max)
|
|
35
|
+
return str;
|
|
36
|
+
return `${str.slice(0, max)}… (+${str.length - max} more chars)`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A short, log-safe preview of model-bound text: collapses whitespace and
|
|
40
|
+
* truncates. Use for prompts/outputs you want visible at info level without
|
|
41
|
+
* dumping the whole payload.
|
|
42
|
+
*/
|
|
43
|
+
export function preview(str, max = 200) {
|
|
44
|
+
const collapsed = str.replace(/\s+/g, ' ').trim();
|
|
45
|
+
return truncate(collapsed, max);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/task/redact.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,GAAG,oGAAoG,CAAC;AAExH,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAc,EAAE,SAAS,GAAG,GAAG,EAAE,OAAO,IAAI,OAAO,EAAU;IAClF,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACjE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE9D,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IACzC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;QACxE,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,GAAG,GAAG,GAAG;IAC7C,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAClC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,MAAM,GAAG,GAAG,cAAc,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,GAAW,EAAE,GAAG,GAAG,GAAG;IAC5C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,OAAO,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC"}
|