@recombine-ai/engine 0.10.2 → 0.10.3-alpha-4
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/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/lib/ai.d.ts +19 -6
- package/build/lib/ai.d.ts.map +1 -1
- package/build/lib/ai.js +16 -6
- package/build/lib/bosun/registry.d.ts +25 -0
- package/build/lib/bosun/registry.d.ts.map +1 -0
- package/build/lib/bosun/registry.js +35 -0
- package/build/lib/bosun/test-workflow.d.ts +24 -0
- package/build/lib/bosun/test-workflow.d.ts.map +1 -0
- package/build/lib/bosun/test-workflow.js +11 -0
- package/build/lib/bosun/workflow.d.ts +25 -0
- package/build/lib/bosun/workflow.d.ts.map +1 -0
- package/build/lib/bosun/workflow.js +6 -0
- package/build/lib/llm-adapters/openai.d.ts +8 -1
- package/build/lib/llm-adapters/openai.d.ts.map +1 -1
- package/build/lib/llm-adapters/openai.js +149 -0
- package/build/lib/platform.d.ts +16 -0
- package/build/lib/platform.d.ts.map +1 -0
- package/build/lib/platform.js +2 -0
- package/build/lib/rc-fs.d.ts +17 -0
- package/build/lib/rc-fs.d.ts.map +1 -0
- package/build/lib/rc-fs.js +22 -0
- package/build/lib/stream-engine.d.ts +73 -0
- package/build/lib/stream-engine.d.ts.map +1 -0
- package/build/lib/stream-engine.js +364 -0
- package/build/lib/test-workflow.d.ts +24 -0
- package/build/lib/test-workflow.d.ts.map +1 -0
- package/build/lib/test-workflow.js +11 -0
- package/package.json +1 -1
package/build/index.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ export * from './lib/interfaces';
|
|
|
4
4
|
export * from './lib/bosun';
|
|
5
5
|
export * from './lib/prompt-fs';
|
|
6
6
|
export * from './lib/llm-adapters';
|
|
7
|
+
export type { AfterResponseCallback, AIStreamEngine, ChatCompletionChunk, LiveTranscript, MainStep, ResponseChunk, WorkFlowConfig, } from './lib/stream-engine';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AAExB,cAAc,gBAAgB,CAAA;AAE9B,cAAc,kBAAkB,CAAA;AAEhC,cAAc,aAAa,CAAA;AAE3B,cAAc,iBAAiB,CAAA;AAE/B,cAAc,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AAExB,cAAc,gBAAgB,CAAA;AAE9B,cAAc,kBAAkB,CAAA;AAEhC,cAAc,aAAa,CAAA;AAE3B,cAAc,iBAAiB,CAAA;AAE/B,cAAc,oBAAoB,CAAA;AAElC,YAAY,EACR,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,aAAa,EACb,cAAc,GACjB,MAAM,qBAAqB,CAAA"}
|
package/build/lib/ai.d.ts
CHANGED
|
@@ -4,11 +4,12 @@ import { SendAction } from './bosun/action';
|
|
|
4
4
|
import { PromptFile } from './prompt-fs';
|
|
5
5
|
import { StepTracer } from './bosun/stepTracer';
|
|
6
6
|
import { StepRegistry, Tracer } from './bosun/tracer';
|
|
7
|
+
import { type AIStreamEngine } from './stream-engine';
|
|
7
8
|
/**
|
|
8
9
|
* Represents a basic model name for LLMs.
|
|
9
10
|
*/
|
|
10
11
|
export type BasicModel = 'o3-mini-2025-01-31' | 'o1-preview-2024-09-12' | 'gpt-4o-2024-08-06' | 'gpt-4o-2024-11-20' | 'gpt-4.1-2025-04-14' | 'o1-2024-12-17' | (string & {});
|
|
11
|
-
export interface LlmAdapter {
|
|
12
|
+
export interface LlmAdapter<StreamChunk = unknown> {
|
|
12
13
|
/**
|
|
13
14
|
* @param systemPrompt – rendered system prompt
|
|
14
15
|
* @param messages – stringified {@link Conversation}
|
|
@@ -16,6 +17,13 @@ export interface LlmAdapter {
|
|
|
16
17
|
* @returns LLM Response
|
|
17
18
|
*/
|
|
18
19
|
generateResponse: (systemPrompt: string, messages: string, schema?: ZodTypeAny) => Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* @param systemPrompt – rendered system prompt
|
|
22
|
+
* @param messages – stringified {@link Conversation}
|
|
23
|
+
* @param schema - optional Zod schema to pass to the model. Will overwrite any schema set in adapter options.
|
|
24
|
+
* @returns LLM Stream
|
|
25
|
+
*/
|
|
26
|
+
streamResponse: (systemPrompt: string, messages: string, schema?: ZodTypeAny) => Promise<AsyncIterable<StreamChunk>>;
|
|
19
27
|
/** Returns adapter's configuration/options for tracing */
|
|
20
28
|
getOptions: () => unknown;
|
|
21
29
|
}
|
|
@@ -214,6 +222,9 @@ export interface AIEngine {
|
|
|
214
222
|
*/
|
|
215
223
|
renderPrompt: typeof renderPrompt;
|
|
216
224
|
}
|
|
225
|
+
export interface AIEngineWithStreaming extends AIEngine {
|
|
226
|
+
createStreamingWorkflow: AIStreamEngine['createWorkflow'];
|
|
227
|
+
}
|
|
217
228
|
/**
|
|
218
229
|
* Represents a conversation between a user and an AI agent.
|
|
219
230
|
* Provides methods to manage the conversation flow, format messages, and convert the conversation
|
|
@@ -322,15 +333,17 @@ export interface EngineConfig {
|
|
|
322
333
|
getToken: () => Promise<string | null>;
|
|
323
334
|
};
|
|
324
335
|
/**
|
|
325
|
-
*
|
|
336
|
+
* Logger instance for handling log messages.
|
|
326
337
|
*/
|
|
327
|
-
logger
|
|
338
|
+
logger: Logger;
|
|
328
339
|
/**
|
|
329
340
|
* Optional function for sending actions.
|
|
341
|
+
*
|
|
342
|
+
* @deprecated SendAction is a legacy integration; prefer workflow-level tracing instead.
|
|
330
343
|
*/
|
|
331
344
|
sendAction?: SendAction;
|
|
332
345
|
/** traces received prompt, rendered prompt, context and other useful info about LLM execution */
|
|
333
|
-
stepTracer
|
|
346
|
+
stepTracer: StepTracer;
|
|
334
347
|
/**
|
|
335
348
|
* registers steps in workflow
|
|
336
349
|
* @deprecated use `stepRegistry` instead
|
|
@@ -339,7 +352,7 @@ export interface EngineConfig {
|
|
|
339
352
|
/**
|
|
340
353
|
* registers steps in workflow
|
|
341
354
|
*/
|
|
342
|
-
stepRegistry
|
|
355
|
+
stepRegistry: StepRegistry;
|
|
343
356
|
}
|
|
344
357
|
/**
|
|
345
358
|
* Creates an AI Engine with the given configuration.
|
|
@@ -366,7 +379,7 @@ export interface EngineConfig {
|
|
|
366
379
|
* const reply = await workflow.run(conversation);
|
|
367
380
|
* ```
|
|
368
381
|
*/
|
|
369
|
-
export declare function createAIEngine(cfg
|
|
382
|
+
export declare function createAIEngine(cfg: EngineConfig): AIEngineWithStreaming;
|
|
370
383
|
declare function renderPrompt(prompt: string, context?: object): string;
|
|
371
384
|
export declare function createConversation(initialMessages?: Message[]): Conversation;
|
|
372
385
|
export declare function getStepBuilder<CTX = unknown>(): StepBuilder<CTX>;
|
package/build/lib/ai.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/lib/ai.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAc,UAAU,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/lib/ai.ts"],"names":[],"mappings":"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"}
|
package/build/lib/ai.js
CHANGED
|
@@ -11,10 +11,10 @@ const crypto_1 = require("crypto");
|
|
|
11
11
|
const nunjucks_1 = __importDefault(require("nunjucks"));
|
|
12
12
|
const zod_1 = require("zod");
|
|
13
13
|
const action_1 = require("./bosun/action");
|
|
14
|
-
const stepTracer_1 = require("./bosun/stepTracer");
|
|
15
14
|
const tracer_1 = require("./bosun/tracer");
|
|
16
15
|
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
17
16
|
const openai_1 = require("./llm-adapters/openai");
|
|
17
|
+
const stream_engine_1 = require("./stream-engine");
|
|
18
18
|
/**
|
|
19
19
|
* Creates an AI Engine with the given configuration.
|
|
20
20
|
*
|
|
@@ -40,11 +40,19 @@ const openai_1 = require("./llm-adapters/openai");
|
|
|
40
40
|
* const reply = await workflow.run(conversation);
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
|
-
function createAIEngine(cfg
|
|
44
|
-
const logger = cfg.logger
|
|
45
|
-
const stepTracer = cfg.stepTracer
|
|
46
|
-
const registry = cfg.stepRegistry || cfg.tracer
|
|
43
|
+
function createAIEngine(cfg) {
|
|
44
|
+
const logger = cfg.logger;
|
|
45
|
+
const stepTracer = cfg.stepTracer;
|
|
46
|
+
const registry = cfg.stepRegistry || cfg.tracer;
|
|
47
|
+
if (!registry) {
|
|
48
|
+
throw new Error('createAIEngine: stepRegistry is required');
|
|
49
|
+
}
|
|
47
50
|
// tokenStorage is used by the default adapter to fetch API keys (backwards compatible)
|
|
51
|
+
const streamAi = (0, stream_engine_1.createStreamingEngine)({
|
|
52
|
+
logger,
|
|
53
|
+
stepTracer,
|
|
54
|
+
stepRegistry: registry,
|
|
55
|
+
});
|
|
48
56
|
function createWorkflow({ onError, steps = [], name = 'workflow', }) {
|
|
49
57
|
steps.forEach(addStepToTracer);
|
|
50
58
|
return {
|
|
@@ -183,14 +191,16 @@ function createAIEngine(cfg = {}) {
|
|
|
183
191
|
: model;
|
|
184
192
|
return adapter.generateResponse(systemPrompt, messages, schema);
|
|
185
193
|
}
|
|
186
|
-
|
|
194
|
+
const engine = {
|
|
187
195
|
createWorkflow,
|
|
188
196
|
createConversation,
|
|
189
197
|
renderPrompt,
|
|
198
|
+
createStreamingWorkflow: streamAi.createWorkflow,
|
|
190
199
|
getStepBuilder() {
|
|
191
200
|
return (step) => step;
|
|
192
201
|
},
|
|
193
202
|
};
|
|
203
|
+
return engine;
|
|
194
204
|
}
|
|
195
205
|
const fallBackTokenStorage = {
|
|
196
206
|
async getToken() {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ZodSchema } from 'zod';
|
|
2
|
+
import { PromptFile } from '../prompt-fs';
|
|
3
|
+
import { Logger } from '../interfaces';
|
|
4
|
+
export interface PromptString {
|
|
5
|
+
type: 'string';
|
|
6
|
+
content: () => Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface StepDef {
|
|
9
|
+
name: string;
|
|
10
|
+
workflow: string;
|
|
11
|
+
type: 'streaming-response' | 'streaming-detect' | 'text';
|
|
12
|
+
prompt?: PromptFile | PromptString;
|
|
13
|
+
schema?: ZodSchema;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkflowDef {
|
|
16
|
+
name: string;
|
|
17
|
+
type: 'stream' | 'text';
|
|
18
|
+
}
|
|
19
|
+
export interface Registry {
|
|
20
|
+
addStep(def: StepDef): void;
|
|
21
|
+
addWorkflow(def: WorkflowDef): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function createConsoleRegistry(logger: Logger): Registry;
|
|
24
|
+
export declare function stdPrompt(prompt: PromptFile | string): PromptString | PromptFile;
|
|
25
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/bosun/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtC,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,oBAAoB,GAAG,kBAAkB,GAAG,MAAM,CAAA;IACxD,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IAClC,MAAM,CAAC,EAAE,SAAS,CAAA;CACrB;AACD,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,QAAQ;IACrB,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAA;CACtC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAoB9D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,6BAQpD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConsoleRegistry = createConsoleRegistry;
|
|
4
|
+
exports.stdPrompt = stdPrompt;
|
|
5
|
+
function createConsoleRegistry(logger) {
|
|
6
|
+
return {
|
|
7
|
+
addStep(def) {
|
|
8
|
+
let msg = `Registry, step added: ${def.name} (${def.type}) to ${def.workflow}`;
|
|
9
|
+
if (def.prompt) {
|
|
10
|
+
if (def.prompt.type === 'string') {
|
|
11
|
+
msg += ' with inline prompt';
|
|
12
|
+
}
|
|
13
|
+
else if (def.prompt.type === 'file') {
|
|
14
|
+
msg += ` with prompt ${def.prompt.path}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (def.schema) {
|
|
18
|
+
msg += ' with schema';
|
|
19
|
+
}
|
|
20
|
+
logger.debug(msg + '.');
|
|
21
|
+
},
|
|
22
|
+
addWorkflow(def) {
|
|
23
|
+
logger.debug('Registry, workflow added:', def.name, `(${def.type})`);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function stdPrompt(prompt) {
|
|
28
|
+
if (typeof prompt === 'string') {
|
|
29
|
+
return {
|
|
30
|
+
type: 'string',
|
|
31
|
+
content: async () => prompt,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return prompt;
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AiEngine } from '../ai';
|
|
2
|
+
import { Logger, Message, Scheduler } from '../interfaces';
|
|
3
|
+
type DefaultContext = Record<string, any>;
|
|
4
|
+
export interface TestWorkflowFactoryProps<CTX extends DefaultContext = DefaultContext> {
|
|
5
|
+
logger: Logger;
|
|
6
|
+
scheduler: Scheduler;
|
|
7
|
+
ai: AiEngine;
|
|
8
|
+
getMessages: (workflow: string) => Message[];
|
|
9
|
+
sendMessage: (workflow: string, message: string) => Promise<void>;
|
|
10
|
+
sendAction: ctx;
|
|
11
|
+
}
|
|
12
|
+
export interface TestWorkflow {
|
|
13
|
+
start: () => Promise<unknown>;
|
|
14
|
+
reactOnMessage: () => Promise<unknown>;
|
|
15
|
+
respondToMessage: () => Promise<unknown>;
|
|
16
|
+
isAssigned: () => Promise<boolean>;
|
|
17
|
+
onFatalError: (error: Error) => Promise<unknown>;
|
|
18
|
+
getContext: () => DefaultContext;
|
|
19
|
+
setContext: (context: DefaultContext) => void;
|
|
20
|
+
}
|
|
21
|
+
export type TestWorkflowFactory = (props: TestWorkflowFactoryProps) => Record<string, TestWorkflow>;
|
|
22
|
+
export declare function createTestWorkflowFactory(creator: TestWorkflowFactory): TestWorkflowFactory;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=test-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-workflow.d.ts","sourceRoot":"","sources":["../../../src/lib/bosun/test-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE1D,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACzC,MAAM,WAAW,wBAAwB,CAAC,GAAG,SAAS,cAAc,GAAG,cAAc;IACjF,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,EAAE,EAAE,QAAQ,CAAA;IACZ,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,EAAE,CAAA;IAC5C,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,UAAU,EACV,GAAG,CAAA;CAAC;AAOR,MAAM,WAAW,YAAY;IAEzB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,UAAU,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAEhD,UAAU,EAAE,MAAM,cAAc,CAAA;IAChC,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;CAChD;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,wBAAwB,KAAK,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AAEnG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,uBAErE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTestWorkflowFactory = createTestWorkflowFactory;
|
|
4
|
+
{
|
|
5
|
+
get: () => CTX;
|
|
6
|
+
set: (ctx) => void subscribe;
|
|
7
|
+
(cb) => void ;
|
|
8
|
+
}
|
|
9
|
+
function createTestWorkflowFactory(creator) {
|
|
10
|
+
return creator;
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AiEngine } from '../ai';
|
|
2
|
+
import { Logger, Message, Scheduler } from '../interfaces';
|
|
3
|
+
import { SendAction } from './action';
|
|
4
|
+
import { Context } from './context';
|
|
5
|
+
type DefaultContext = Record<string, any>;
|
|
6
|
+
export interface TestWorkflowFactoryProps<CTX extends DefaultContext = DefaultContext> {
|
|
7
|
+
logger: Logger;
|
|
8
|
+
scheduler: Scheduler;
|
|
9
|
+
ai: AiEngine;
|
|
10
|
+
getMessages: () => Message[];
|
|
11
|
+
sendMessage: (message: string) => Promise<void>;
|
|
12
|
+
sendAction: SendAction;
|
|
13
|
+
ctx: Context<CTX>;
|
|
14
|
+
}
|
|
15
|
+
export interface TestWorkflow {
|
|
16
|
+
start: () => Promise<unknown>;
|
|
17
|
+
reactOnMessage: () => Promise<unknown>;
|
|
18
|
+
respondToMessage: () => Promise<unknown>;
|
|
19
|
+
isAssigned: () => Promise<boolean>;
|
|
20
|
+
onFatalError: (error: Error) => Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
export type TestWorkflowFactory<T extends DefaultContext = DefaultContext> = (props: TestWorkflowFactoryProps<T>) => TestWorkflow;
|
|
23
|
+
export declare function createTestWorkflowFactory<T extends DefaultContext>(creator: TestWorkflowFactory<T>): TestWorkflowFactory<T>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../../src/lib/bosun/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACzC,MAAM,WAAW,wBAAwB,CAAC,GAAG,SAAS,cAAc,GAAG,cAAc;IACjF,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,EAAE,EAAE,QAAQ,CAAA;IACZ,WAAW,EAAE,MAAM,OAAO,EAAE,CAAA;IAC5B,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,UAAU,EAAE,UAAU,CAAA;IACtB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,UAAU,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACnD;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI,CACzE,KAAK,EAAE,wBAAwB,CAAC,CAAC,CAAC,KACjC,YAAY,CAAA;AAEjB,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,cAAc,EAC9D,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,0BAGlC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatCompletionCreateParamsBase } from 'openai/resources/chat/completions';
|
|
2
|
+
import type { ChatCompletionChunk } from 'openai/resources/chat/completions';
|
|
2
3
|
import type { LlmAdapter } from '../ai';
|
|
3
4
|
export type OpenAIChatOptions = Omit<ChatCompletionCreateParamsBase, 'messages' | 'stream'>;
|
|
4
5
|
export type OpenAIAdapterAuth = {
|
|
@@ -6,5 +7,11 @@ export type OpenAIAdapterAuth = {
|
|
|
6
7
|
getToken: () => Promise<string | null>;
|
|
7
8
|
};
|
|
8
9
|
};
|
|
9
|
-
export
|
|
10
|
+
export type AzureOpenAIAdapterConfig = {
|
|
11
|
+
endpoint: string;
|
|
12
|
+
apiVersion: string;
|
|
13
|
+
deployment?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function createOpenAIAdapter(options: OpenAIChatOptions, auth: OpenAIAdapterAuth): LlmAdapter<ChatCompletionChunk>;
|
|
16
|
+
export declare function createAzureOpenAIAdapter(options: OpenAIChatOptions, azure: AzureOpenAIAdapterConfig, auth: OpenAIAdapterAuth): LlmAdapter<ChatCompletionChunk>;
|
|
10
17
|
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/openai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAA;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAIvC,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,8BAA8B,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAA;AAE3F,MAAM,MAAM,iBAAiB,GAAG;IAC5B,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;KAAE,CAAA;CAC3D,CAAA;AAID,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,iBAAiB,GACxB,UAAU,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/openai.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAA;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAA;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAIvC,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,8BAA8B,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAA;AAE3F,MAAM,MAAM,iBAAiB,GAAG;IAC5B,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;KAAE,CAAA;CAC3D,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAID,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,iBAAiB,GACxB,UAAU,CAAC,mBAAmB,CAAC,CAuGjC;AAED,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,wBAAwB,EAC/B,IAAI,EAAE,iBAAiB,GACxB,UAAU,CAAC,mBAAmB,CAAC,CAiHjC"}
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createOpenAIAdapter = createOpenAIAdapter;
|
|
7
|
+
exports.createAzureOpenAIAdapter = createAzureOpenAIAdapter;
|
|
7
8
|
const openai_1 = require("openai");
|
|
8
9
|
const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
|
|
9
10
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -47,5 +48,153 @@ function createOpenAIAdapter(options, auth) {
|
|
|
47
48
|
}
|
|
48
49
|
return content;
|
|
49
50
|
},
|
|
51
|
+
async streamResponse(systemPrompt, messages, schema) {
|
|
52
|
+
// TODO: This adapter is copy-pasted from an earlier implementation; consolidate shared logic with `generateResponse`.
|
|
53
|
+
const finalOptions = { ...options };
|
|
54
|
+
if (schema) {
|
|
55
|
+
finalOptions.response_format = {
|
|
56
|
+
type: 'json_schema',
|
|
57
|
+
json_schema: {
|
|
58
|
+
name: 'detector_response',
|
|
59
|
+
schema: (0, zod_to_json_schema_1.default)(schema),
|
|
60
|
+
strict: true,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const apiKey = await auth.tokenStorage.getToken();
|
|
65
|
+
if (!apiKey) {
|
|
66
|
+
throw new Error('OpenAI API key is not set');
|
|
67
|
+
}
|
|
68
|
+
if (apiKey === '__TESTING__') {
|
|
69
|
+
await delay(100);
|
|
70
|
+
const cannedResponse = schema
|
|
71
|
+
? JSON.stringify({ message: 'canned response', reasons: [] })
|
|
72
|
+
: 'canned response';
|
|
73
|
+
return (async function* () {
|
|
74
|
+
yield {
|
|
75
|
+
id: 'chatcmpl-testing',
|
|
76
|
+
object: 'chat.completion.chunk',
|
|
77
|
+
created: Math.floor(Date.now() / 1000),
|
|
78
|
+
model: finalOptions.model,
|
|
79
|
+
choices: [
|
|
80
|
+
{
|
|
81
|
+
index: 0,
|
|
82
|
+
finish_reason: null,
|
|
83
|
+
delta: { content: cannedResponse },
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
})();
|
|
88
|
+
}
|
|
89
|
+
const client = new openai_1.OpenAI({ apiKey });
|
|
90
|
+
return await client.chat.completions.create({
|
|
91
|
+
messages: [
|
|
92
|
+
{ role: 'system', content: systemPrompt },
|
|
93
|
+
{ role: 'user', content: messages },
|
|
94
|
+
],
|
|
95
|
+
...finalOptions,
|
|
96
|
+
stream: true,
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function createAzureOpenAIAdapter(options, azure, auth) {
|
|
102
|
+
return {
|
|
103
|
+
getOptions: () => ({ ...options, azure }),
|
|
104
|
+
async generateResponse(systemPrompt, messages, schema) {
|
|
105
|
+
const finalOptions = { ...options };
|
|
106
|
+
if (schema) {
|
|
107
|
+
finalOptions.response_format = {
|
|
108
|
+
type: 'json_schema',
|
|
109
|
+
json_schema: {
|
|
110
|
+
name: 'detector_response',
|
|
111
|
+
schema: (0, zod_to_json_schema_1.default)(schema),
|
|
112
|
+
strict: true,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const apiKey = await auth.tokenStorage.getToken();
|
|
117
|
+
if (!apiKey) {
|
|
118
|
+
throw new Error('OpenAI API key is not set');
|
|
119
|
+
}
|
|
120
|
+
if (apiKey === '__TESTING__') {
|
|
121
|
+
await delay(100);
|
|
122
|
+
if (options.response_format && 'json_schema' in options.response_format) {
|
|
123
|
+
return JSON.stringify({ message: 'canned response', reasons: [] });
|
|
124
|
+
}
|
|
125
|
+
return 'canned response';
|
|
126
|
+
}
|
|
127
|
+
const client = new openai_1.AzureOpenAI({
|
|
128
|
+
apiKey,
|
|
129
|
+
apiVersion: azure.apiVersion,
|
|
130
|
+
endpoint: azure.endpoint,
|
|
131
|
+
deployment: azure.deployment,
|
|
132
|
+
});
|
|
133
|
+
const response = await client.chat.completions.create({
|
|
134
|
+
messages: [
|
|
135
|
+
{ role: 'system', content: systemPrompt },
|
|
136
|
+
{ role: 'user', content: messages },
|
|
137
|
+
],
|
|
138
|
+
...finalOptions,
|
|
139
|
+
});
|
|
140
|
+
const content = response.choices[0]?.message?.content;
|
|
141
|
+
if (!content) {
|
|
142
|
+
throw new Error('No response from OpenAI');
|
|
143
|
+
}
|
|
144
|
+
return content;
|
|
145
|
+
},
|
|
146
|
+
async streamResponse(systemPrompt, messages, schema) {
|
|
147
|
+
// TODO: This adapter is copy-pasted from an earlier implementation; consolidate shared logic with `generateResponse`.
|
|
148
|
+
const finalOptions = { ...options };
|
|
149
|
+
if (schema) {
|
|
150
|
+
finalOptions.response_format = {
|
|
151
|
+
type: 'json_schema',
|
|
152
|
+
json_schema: {
|
|
153
|
+
name: 'detector_response',
|
|
154
|
+
schema: (0, zod_to_json_schema_1.default)(schema),
|
|
155
|
+
strict: true,
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const apiKey = await auth.tokenStorage.getToken();
|
|
160
|
+
if (!apiKey) {
|
|
161
|
+
throw new Error('OpenAI API key is not set');
|
|
162
|
+
}
|
|
163
|
+
if (apiKey === '__TESTING__') {
|
|
164
|
+
await delay(100);
|
|
165
|
+
const cannedResponse = schema
|
|
166
|
+
? JSON.stringify({ message: 'canned response', reasons: [] })
|
|
167
|
+
: 'canned response';
|
|
168
|
+
return (async function* () {
|
|
169
|
+
yield {
|
|
170
|
+
id: 'chatcmpl-testing',
|
|
171
|
+
object: 'chat.completion.chunk',
|
|
172
|
+
created: Math.floor(Date.now() / 1000),
|
|
173
|
+
model: finalOptions.model,
|
|
174
|
+
choices: [
|
|
175
|
+
{
|
|
176
|
+
index: 0,
|
|
177
|
+
finish_reason: null,
|
|
178
|
+
delta: { content: cannedResponse },
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
})();
|
|
183
|
+
}
|
|
184
|
+
const client = new openai_1.AzureOpenAI({
|
|
185
|
+
apiKey,
|
|
186
|
+
apiVersion: azure.apiVersion,
|
|
187
|
+
endpoint: azure.endpoint,
|
|
188
|
+
deployment: azure.deployment,
|
|
189
|
+
});
|
|
190
|
+
return await client.chat.completions.create({
|
|
191
|
+
messages: [
|
|
192
|
+
{ role: 'system', content: systemPrompt },
|
|
193
|
+
{ role: 'user', content: messages },
|
|
194
|
+
],
|
|
195
|
+
...finalOptions,
|
|
196
|
+
stream: true,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
50
199
|
};
|
|
51
200
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AIEngine } from './ai';
|
|
2
|
+
import { Logger } from './interfaces';
|
|
3
|
+
import { PromptFS } from './prompt-fs';
|
|
4
|
+
export interface Platform<TConf = {}> {
|
|
5
|
+
/** Streaming AI engine, used to create streaming workflows to be used in voice agents */
|
|
6
|
+
streamAi: any;
|
|
7
|
+
/** String-base AI engine, used in text agents or for analysis of the conversation */
|
|
8
|
+
ai: AIEngine;
|
|
9
|
+
/** File system, used to load prompt files */
|
|
10
|
+
fs: PromptFS;
|
|
11
|
+
/** Handles to mange calls, e.g. re-run stream response, hangup, etc. */
|
|
12
|
+
rcVoice: any;
|
|
13
|
+
/** Platform and agent-specific configuration variables, e.g. trunk setup, voice settings etc. */
|
|
14
|
+
logger: Logger;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/lib/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,MAAM,WAAW,QAAQ,CAAC,KAAK,GAAG,EAAE;IAChC,yFAAyF;IAEzF,QAAQ,EAAE,GAAG,CAAA;IACb,qFAAqF;IACrF,EAAE,EAAE,QAAQ,CAAA;IACZ,6CAA6C;IAC7C,EAAE,EAAE,QAAQ,CAAA;IACZ,wEAAwE;IAExE,OAAO,EAAE,GAAG,CAAA;IACZ,iGAAiG;IAEjG,MAAM,EAAE,MAAM,CAAA;CACjB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Logger } from '@recombine-ai/engine';
|
|
2
|
+
export interface FsConfig {
|
|
3
|
+
logger: Logger;
|
|
4
|
+
basePath: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RcFS {
|
|
7
|
+
loadFile(path: string): PromptFile;
|
|
8
|
+
}
|
|
9
|
+
export declare function createLocalFS(cfg: FsConfig): {
|
|
10
|
+
loadFile(path: string): PromptFile;
|
|
11
|
+
};
|
|
12
|
+
export interface PromptFile {
|
|
13
|
+
type: 'file';
|
|
14
|
+
content: () => Promise<string>;
|
|
15
|
+
path: string;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=rc-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-fs.d.ts","sourceRoot":"","sources":["../../src/lib/rc-fs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAI7C,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,IAAI;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAA;CACrC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ;mBAEpB,MAAM,GAAG,UAAU;EAWzC;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;CACf"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createLocalFS = createLocalFS;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
function createLocalFS(cfg) {
|
|
10
|
+
return {
|
|
11
|
+
loadFile(path) {
|
|
12
|
+
return {
|
|
13
|
+
type: 'file',
|
|
14
|
+
content: async () => {
|
|
15
|
+
cfg.logger.debug('AI Engine: loading prompt:', path);
|
|
16
|
+
return fs_1.default.promises.readFile((0, path_1.join)(cfg.basePath, path), 'utf-8');
|
|
17
|
+
},
|
|
18
|
+
path,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ReadableStream } from 'node:stream/web';
|
|
2
|
+
import type { LlmAdapter, Message } from './ai';
|
|
3
|
+
import type { ConversationalTrace, ConversationalTracer } from './bosun/conversationalTracer';
|
|
4
|
+
import { type StepRegistry } from './bosun/tracer';
|
|
5
|
+
import type { StepTracer } from './bosun/stepTracer';
|
|
6
|
+
import type { Logger } from './interfaces';
|
|
7
|
+
import type { PromptFile } from './prompt-fs';
|
|
8
|
+
export type ChatCompletionChunk = {
|
|
9
|
+
choices: Array<{
|
|
10
|
+
delta?: {
|
|
11
|
+
content?: string | null;
|
|
12
|
+
} | null;
|
|
13
|
+
}>;
|
|
14
|
+
};
|
|
15
|
+
export interface MainStep {
|
|
16
|
+
name: string;
|
|
17
|
+
responsePrompt: string | PromptFile;
|
|
18
|
+
model: LlmAdapter<ChatCompletionChunk>;
|
|
19
|
+
}
|
|
20
|
+
interface ProgrammaticFilter {
|
|
21
|
+
shouldStartFiltering: (state: LiveTranscript, newToken: string) => boolean;
|
|
22
|
+
onNewToken: (state: LiveTranscript, filteredTokens: string[]) => {
|
|
23
|
+
action: 'CONTINUE_FILTERING';
|
|
24
|
+
} | {
|
|
25
|
+
action: 'RELEASE_TOKENS';
|
|
26
|
+
tokens: string[];
|
|
27
|
+
};
|
|
28
|
+
onStreamEnd: (state: LiveTranscript, filteredTokens: string[]) => {
|
|
29
|
+
tokensToRelease: string[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface ResponseChunk {
|
|
33
|
+
role: 'agent' | 'system';
|
|
34
|
+
delta: string;
|
|
35
|
+
}
|
|
36
|
+
export interface WorkFlowConfig<CTX extends {}> {
|
|
37
|
+
workflowId: string;
|
|
38
|
+
main: MainStep;
|
|
39
|
+
programmaticFilter?: ProgrammaticFilter;
|
|
40
|
+
afterResponseDelayMs?: number;
|
|
41
|
+
conversationalTracer: ConversationalTracer;
|
|
42
|
+
conversationalTraceMedium: ConversationalTrace['medium'];
|
|
43
|
+
onQuotaExceeded?: (error: Error) => Promise<void>;
|
|
44
|
+
onMainResponseFinished?: () => Promise<void>;
|
|
45
|
+
onError: (error: Error | string, ctx: CTX) => Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export type AfterResponseCallback<CTX extends {}> = (conversation: LiveTranscript, ctx: CTX) => Promise<void>;
|
|
48
|
+
export interface AIStreamEngine {
|
|
49
|
+
createWorkflow: <CTX extends {}>(config: WorkFlowConfig<CTX>) => {
|
|
50
|
+
run: (callId: string, messages: Message[], ctx: CTX) => Promise<ReadableStream<ResponseChunk>>;
|
|
51
|
+
afterResponse: (callback: AfterResponseCallback<CTX>) => void;
|
|
52
|
+
};
|
|
53
|
+
loadFile: (path: string) => PromptFile;
|
|
54
|
+
createMainStep: (step: MainStep) => MainStep;
|
|
55
|
+
}
|
|
56
|
+
export interface LiveTranscript {
|
|
57
|
+
responseChunks: ResponseChunk[];
|
|
58
|
+
readonly currentResponse: string;
|
|
59
|
+
readonly mainResponseFinished: boolean;
|
|
60
|
+
readonly messages: Message[];
|
|
61
|
+
markMainResponseFinished(): void;
|
|
62
|
+
toString(ignoreDirectives?: boolean): string;
|
|
63
|
+
getConversation(): Message[];
|
|
64
|
+
}
|
|
65
|
+
type StreamingEngineConfig = {
|
|
66
|
+
logger: Logger;
|
|
67
|
+
stepTracer: StepTracer;
|
|
68
|
+
stepRegistry: StepRegistry;
|
|
69
|
+
basePath?: string;
|
|
70
|
+
};
|
|
71
|
+
export declare function createStreamingEngine(cfg: StreamingEngineConfig): AIStreamEngine;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=stream-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream-engine.d.ts","sourceRoot":"","sources":["../../src/lib/stream-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createStreamingEngine = createStreamingEngine;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const web_1 = require("node:stream/web");
|
|
10
|
+
const nunjucks_1 = __importDefault(require("nunjucks"));
|
|
11
|
+
const tracer_1 = require("./bosun/tracer");
|
|
12
|
+
// callId -> runId
|
|
13
|
+
const currentRunsStore = new Map();
|
|
14
|
+
function createStreamingEngine(cfg) {
|
|
15
|
+
const basePath = cfg.basePath ?? process.cwd();
|
|
16
|
+
cfg.logger.debug(`Base path is: ${(0, node_path_1.resolve)(basePath)}`);
|
|
17
|
+
const stepRegistry = cfg.stepRegistry;
|
|
18
|
+
function createWorkflow({ workflowId, main, programmaticFilter, afterResponseDelayMs = 2000, conversationalTracer, conversationalTraceMedium, onQuotaExceeded, onMainResponseFinished, onError, }) {
|
|
19
|
+
cfg.logger.debug('streamAiEngine.createWorkflow');
|
|
20
|
+
stepRegistry.addStep({
|
|
21
|
+
type: 'streaming-response',
|
|
22
|
+
name: main.name,
|
|
23
|
+
prompt: (0, tracer_1.stdPrompt)(main.responsePrompt),
|
|
24
|
+
});
|
|
25
|
+
const afterResponseCallbacks = [];
|
|
26
|
+
async function run(callId, messages, ctx) {
|
|
27
|
+
let streamCancelled = false;
|
|
28
|
+
let innerStream = null;
|
|
29
|
+
return new web_1.ReadableStream({
|
|
30
|
+
cancel: (reason) => {
|
|
31
|
+
cfg.logger.debug(`streamingWorkflow.run cancelled: ${reason}`);
|
|
32
|
+
streamCancelled = true;
|
|
33
|
+
if (innerStream) {
|
|
34
|
+
innerStream.cancel(reason).catch(() => { });
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
start: async (controller) => {
|
|
38
|
+
try {
|
|
39
|
+
innerStream = await generateResponseStream(callId, messages, ctx);
|
|
40
|
+
for await (const chunk of innerStream) {
|
|
41
|
+
if (streamCancelled)
|
|
42
|
+
return;
|
|
43
|
+
controller.enqueue(chunk);
|
|
44
|
+
}
|
|
45
|
+
if (streamCancelled)
|
|
46
|
+
return;
|
|
47
|
+
controller.close();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (streamCancelled) {
|
|
51
|
+
cfg.logger.debug('streamingWorkflow.run will not propagate error due to stream cancellation', e);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const err = normalizeError(e);
|
|
55
|
+
controller.error(err);
|
|
56
|
+
if (isQuotaExceededError(err) && onQuotaExceeded) {
|
|
57
|
+
await onQuotaExceeded(err);
|
|
58
|
+
}
|
|
59
|
+
await onError(err, ctx);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function generateResponseStream(callId, messages, ctx) {
|
|
65
|
+
const runId = crypto.randomUUID();
|
|
66
|
+
cfg.logger.debug(`streamingWorkflow.run callId: ${callId}, runId: ${runId}`);
|
|
67
|
+
currentRunsStore.set(callId, runId);
|
|
68
|
+
const startTime = performance.now();
|
|
69
|
+
const transcript = createConversation(messages, cfg.logger);
|
|
70
|
+
let currentFilter = null;
|
|
71
|
+
let filteredTokens = [];
|
|
72
|
+
const model = main.model;
|
|
73
|
+
const modelName = getModelName(model);
|
|
74
|
+
const mainStepTrace = {
|
|
75
|
+
workflowId,
|
|
76
|
+
workflowRunId: runId,
|
|
77
|
+
name: main.name,
|
|
78
|
+
model: modelName,
|
|
79
|
+
conversationId: callId,
|
|
80
|
+
receivedContext: ctx,
|
|
81
|
+
receivedPrompt: main.responsePrompt,
|
|
82
|
+
createdAt: Date.now(),
|
|
83
|
+
response: '',
|
|
84
|
+
};
|
|
85
|
+
const renderedPrompt = await renderPrompt(main.responsePrompt, ctx);
|
|
86
|
+
const stringifiedConversation = transcript.toString();
|
|
87
|
+
mainStepTrace.renderedPrompt = renderedPrompt;
|
|
88
|
+
mainStepTrace.stringifiedConversation = stringifiedConversation;
|
|
89
|
+
let llmStream;
|
|
90
|
+
try {
|
|
91
|
+
llmStream = await runLLMStream({
|
|
92
|
+
systemPrompt: renderedPrompt,
|
|
93
|
+
messages: stringifiedConversation,
|
|
94
|
+
model,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
mainStepTrace.error = normalizeError(err);
|
|
99
|
+
cfg.stepTracer.addStepTrace(mainStepTrace);
|
|
100
|
+
await cfg.stepTracer.flush();
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
cfg.logger.debug(`[MARK] stream created: ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
104
|
+
let measureTtft = true;
|
|
105
|
+
let streamCancelled = false;
|
|
106
|
+
return new web_1.ReadableStream({
|
|
107
|
+
cancel: (reason) => {
|
|
108
|
+
cfg.logger.debug(`streamingWorkflow.generateResponseStream cancelled: ${reason}`);
|
|
109
|
+
streamCancelled = true;
|
|
110
|
+
if (currentRunsStore.get(callId) === runId) {
|
|
111
|
+
currentRunsStore.delete(callId);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
async start(controller) {
|
|
115
|
+
try {
|
|
116
|
+
cfg.logger.debug('streamingWorkflow.run: starting main stream');
|
|
117
|
+
for await (const chunk of llmStream) {
|
|
118
|
+
if (streamCancelled)
|
|
119
|
+
break;
|
|
120
|
+
if (measureTtft) {
|
|
121
|
+
cfg.logger.debug(`[MARK] TTFT: ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
122
|
+
measureTtft = false;
|
|
123
|
+
}
|
|
124
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
125
|
+
if (!delta) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
mainStepTrace.response = (mainStepTrace.response || '') + delta;
|
|
129
|
+
let tokensToRelease = [];
|
|
130
|
+
if (currentFilter) {
|
|
131
|
+
filteredTokens.push(delta);
|
|
132
|
+
const filterResult = currentFilter.onNewToken(transcript, filteredTokens);
|
|
133
|
+
if (filterResult.action === 'RELEASE_TOKENS') {
|
|
134
|
+
cfg.logger.debug('streamingWorkflow.run: programmatic filter releasing tokens', { tokens: filterResult.tokens });
|
|
135
|
+
tokensToRelease = filterResult.tokens;
|
|
136
|
+
currentFilter = null;
|
|
137
|
+
filteredTokens = [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (programmaticFilter?.shouldStartFiltering(transcript, delta)) {
|
|
141
|
+
filteredTokens = [delta];
|
|
142
|
+
currentFilter = programmaticFilter;
|
|
143
|
+
cfg.logger.debug('streamingWorkflow.run: programmatic filter is applied on token', { delta });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
tokensToRelease = [delta];
|
|
147
|
+
}
|
|
148
|
+
releaseMainStreamTokens(tokensToRelease);
|
|
149
|
+
}
|
|
150
|
+
if (streamCancelled) {
|
|
151
|
+
cfg.logger.debug(`streamingWorkflow.run: stream cancelled for run ${runId}, skipping traces and afterResponse`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (currentFilter) {
|
|
155
|
+
releaseMainStreamTokens(currentFilter.onStreamEnd(transcript, filteredTokens)
|
|
156
|
+
.tokensToRelease);
|
|
157
|
+
currentFilter = null;
|
|
158
|
+
filteredTokens = [];
|
|
159
|
+
}
|
|
160
|
+
cfg.logger.debug('streamingWorkflow.run: markMainResponseFinished');
|
|
161
|
+
cfg.logger.debug(`[MARK] finished: ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
162
|
+
transcript.markMainResponseFinished();
|
|
163
|
+
cfg.stepTracer.addStepTrace(mainStepTrace);
|
|
164
|
+
await cfg.stepTracer.flush();
|
|
165
|
+
await safeAddFinishedMainStepTrace(conversationalTracer, {
|
|
166
|
+
conversationId: callId,
|
|
167
|
+
content: transcript.currentResponse,
|
|
168
|
+
medium: conversationalTraceMedium,
|
|
169
|
+
});
|
|
170
|
+
await onMainResponseFinished?.();
|
|
171
|
+
controller.close();
|
|
172
|
+
if (currentRunsStore.get(callId) !== runId) {
|
|
173
|
+
cfg.logger.debug(`streamingWorkflow.run: finished main stream for run ${runId}, but a new run has started in parallel. Will not run afterResponse callbacks.`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
cfg.logger.debug(`streamingWorkflow.run: finished main stream for run ${runId}, running afterResponse callbacks in ${afterResponseDelayMs}ms`);
|
|
177
|
+
setTimeout(() => {
|
|
178
|
+
void runAfterResponseCallbacks(callId, runId, transcript, ctx);
|
|
179
|
+
}, afterResponseDelayMs);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
if (streamCancelled) {
|
|
183
|
+
cfg.logger.debug('streamingWorkflow.generateResponseStream will not propagate error due to stream cancellation', err);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
mainStepTrace.error = normalizeError(err);
|
|
187
|
+
cfg.stepTracer.addStepTrace(mainStepTrace);
|
|
188
|
+
await cfg.stepTracer.flush();
|
|
189
|
+
if (currentRunsStore.get(callId) === runId) {
|
|
190
|
+
currentRunsStore.delete(callId);
|
|
191
|
+
}
|
|
192
|
+
controller.error(normalizeError(err));
|
|
193
|
+
}
|
|
194
|
+
function releaseMainStreamTokens(tokens) {
|
|
195
|
+
for (const token of tokens) {
|
|
196
|
+
if (streamCancelled) {
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
transcript.responseChunks.push({ role: 'agent', delta: token });
|
|
200
|
+
controller.enqueue({ role: 'agent', delta: token });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function runAfterResponseCallbacks(callId, runId, transcript, ctx) {
|
|
207
|
+
if (currentRunsStore.get(callId) !== runId) {
|
|
208
|
+
cfg.logger.debug(`streamingWorkflow.run ${runId} is no longer the latest run id, skipping afterResponse`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
cfg.logger.debug(`streamingWorkflow.run running afterResponse callbacks for run ${runId}`);
|
|
213
|
+
for (const cb of afterResponseCallbacks) {
|
|
214
|
+
await cb(transcript, ctx);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
cfg.logger.error('Error during afterResponse processing', { err });
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
cfg.logger.debug(`streamingWorkflow.run finished afterResponse callbacks for run ${runId}, removing it from the store`);
|
|
222
|
+
if (currentRunsStore.get(callId) === runId) {
|
|
223
|
+
currentRunsStore.delete(callId);
|
|
224
|
+
}
|
|
225
|
+
cfg.stepTracer.flush().catch((err) => {
|
|
226
|
+
cfg.logger.error('Failed to flush step traces after afterResponse', { err });
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function runLLMStream(input) {
|
|
231
|
+
cfg.logger.debug('AI Engine Stream: starting llm stream');
|
|
232
|
+
return await input.model.streamResponse(input.systemPrompt, input.messages);
|
|
233
|
+
}
|
|
234
|
+
async function renderPrompt(prompt, context) {
|
|
235
|
+
const template = typeof prompt === 'string' ? prompt : await prompt.content();
|
|
236
|
+
nunjucks_1.default.configure({
|
|
237
|
+
autoescape: true,
|
|
238
|
+
trimBlocks: true,
|
|
239
|
+
lstripBlocks: true,
|
|
240
|
+
});
|
|
241
|
+
return nunjucks_1.default.renderString(template, context);
|
|
242
|
+
}
|
|
243
|
+
async function safeAddFinishedMainStepTrace(tracer, input) {
|
|
244
|
+
try {
|
|
245
|
+
tracer.addConversationalTrace({
|
|
246
|
+
conversationId: input.conversationId,
|
|
247
|
+
eventName: 'finished-main-streaming-step',
|
|
248
|
+
role: 'agent',
|
|
249
|
+
medium: input.medium,
|
|
250
|
+
content: input.content,
|
|
251
|
+
createdAt: Date.now(),
|
|
252
|
+
});
|
|
253
|
+
await tracer.flush();
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
cfg.logger.error('Failed to write conversational trace', { err });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function isQuotaExceededError(err) {
|
|
260
|
+
if (!(err instanceof Error))
|
|
261
|
+
return false;
|
|
262
|
+
if (err.name === 'RateLimitError')
|
|
263
|
+
return true;
|
|
264
|
+
if (hasStatus(err) && err.status === 429)
|
|
265
|
+
return true;
|
|
266
|
+
return err.constructor?.name === 'RateLimitError';
|
|
267
|
+
}
|
|
268
|
+
function hasStatus(value) {
|
|
269
|
+
return 'status' in value;
|
|
270
|
+
}
|
|
271
|
+
function getModelName(model) {
|
|
272
|
+
const options = model.getOptions();
|
|
273
|
+
if (!options || typeof options !== 'object')
|
|
274
|
+
return 'unknown';
|
|
275
|
+
const maybeModel = options.model;
|
|
276
|
+
return typeof maybeModel === 'string' ? maybeModel : 'unknown';
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
run,
|
|
280
|
+
afterResponse: (cb) => {
|
|
281
|
+
afterResponseCallbacks.push(cb);
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function loadFile(path) {
|
|
286
|
+
return {
|
|
287
|
+
type: 'file',
|
|
288
|
+
path,
|
|
289
|
+
content: async () => {
|
|
290
|
+
cfg.logger.debug('loading prompt:', path);
|
|
291
|
+
return node_fs_1.default.promises.readFile((0, node_path_1.join)(basePath, path), 'utf-8');
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
createWorkflow,
|
|
297
|
+
loadFile,
|
|
298
|
+
createMainStep,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function normalizeError(err) {
|
|
302
|
+
if (err instanceof Error)
|
|
303
|
+
return err;
|
|
304
|
+
if (typeof err === 'string')
|
|
305
|
+
return new Error(err);
|
|
306
|
+
try {
|
|
307
|
+
return new Error(JSON.stringify(err));
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return new Error(String(err));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function createConversation(initialMessages, logger) {
|
|
314
|
+
let finished = false;
|
|
315
|
+
const messages = [...initialMessages];
|
|
316
|
+
const directivesFormatter = (msg) => `${msg.sender}: ${msg.text}`;
|
|
317
|
+
const currentResponseFormatter = (partial) => `Current response: ${partial}`;
|
|
318
|
+
const responseTokens = [];
|
|
319
|
+
const names = {
|
|
320
|
+
agent: 'Agent',
|
|
321
|
+
user: 'User',
|
|
322
|
+
system: 'System',
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
responseChunks: responseTokens,
|
|
326
|
+
get currentResponse() {
|
|
327
|
+
return responseTokens
|
|
328
|
+
.filter((token) => token.role === 'agent')
|
|
329
|
+
.map((token) => token.delta)
|
|
330
|
+
.join('');
|
|
331
|
+
},
|
|
332
|
+
get mainResponseFinished() {
|
|
333
|
+
return finished;
|
|
334
|
+
},
|
|
335
|
+
get messages() {
|
|
336
|
+
return messages;
|
|
337
|
+
},
|
|
338
|
+
markMainResponseFinished() {
|
|
339
|
+
logger.debug('AIEngine.Conversation, markMainResponseFinished');
|
|
340
|
+
finished = true;
|
|
341
|
+
},
|
|
342
|
+
toString(ignoreDirectives = false) {
|
|
343
|
+
const serializedMessages = messages
|
|
344
|
+
.map((msg) => {
|
|
345
|
+
if (msg.sender === 'system') {
|
|
346
|
+
return ignoreDirectives ? null : directivesFormatter(msg);
|
|
347
|
+
}
|
|
348
|
+
return `${names[msg.sender]}: ${msg.text}`;
|
|
349
|
+
})
|
|
350
|
+
.filter((line) => line !== null)
|
|
351
|
+
.join('\n');
|
|
352
|
+
if (responseTokens.length === 0) {
|
|
353
|
+
return serializedMessages;
|
|
354
|
+
}
|
|
355
|
+
return serializedMessages + '\n' + currentResponseFormatter(this.currentResponse);
|
|
356
|
+
},
|
|
357
|
+
getConversation() {
|
|
358
|
+
return messages;
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function createMainStep(step) {
|
|
363
|
+
return step;
|
|
364
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AiEngine } from './ai';
|
|
2
|
+
import { Logger, Message, Scheduler } from './interfaces';
|
|
3
|
+
type DefaultContext = Record<string, any>;
|
|
4
|
+
export interface TestWorkflowFactoryProps<CTX extends DefaultContext = DefaultContext> {
|
|
5
|
+
logger: Logger;
|
|
6
|
+
scheduler: Scheduler;
|
|
7
|
+
ai: AiEngine;
|
|
8
|
+
getMessages: (workflow: string) => Message[];
|
|
9
|
+
sendMessage: (workflow: string, message: string) => Promise<void>;
|
|
10
|
+
sendAction: ctx;
|
|
11
|
+
}
|
|
12
|
+
export interface TestWorkflow {
|
|
13
|
+
start: () => Promise<unknown>;
|
|
14
|
+
reactOnMessage: () => Promise<unknown>;
|
|
15
|
+
respondToMessage: () => Promise<unknown>;
|
|
16
|
+
isAssigned: () => Promise<boolean>;
|
|
17
|
+
onFatalError: (error: Error) => Promise<unknown>;
|
|
18
|
+
getContext: () => DefaultContext;
|
|
19
|
+
setContext: (context: DefaultContext) => void;
|
|
20
|
+
}
|
|
21
|
+
export type TestWorkflowFactory = (props: TestWorkflowFactoryProps) => Record<string, TestWorkflow>;
|
|
22
|
+
export declare function createTestWorkflowFactory(creator: TestWorkflowFactory): TestWorkflowFactory;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=test-workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-workflow.d.ts","sourceRoot":"","sources":["../../src/lib/test-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAEzD,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACzC,MAAM,WAAW,wBAAwB,CAAC,GAAG,SAAS,cAAc,GAAG,cAAc;IACjF,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,EAAE,EAAE,QAAQ,CAAA;IACZ,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,EAAE,CAAA;IAC5C,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,UAAU,EACV,GAAG,CAAA;CAAC;AAOR,MAAM,WAAW,YAAY;IAEzB,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,gBAAgB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,UAAU,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAEhD,UAAU,EAAE,MAAM,cAAc,CAAA;IAChC,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;CAChD;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,wBAAwB,KAAK,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AAEnG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,mBAAmB,uBAErE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTestWorkflowFactory = createTestWorkflowFactory;
|
|
4
|
+
{
|
|
5
|
+
get: () => CTX;
|
|
6
|
+
set: (ctx) => void subscribe;
|
|
7
|
+
(cb) => void ;
|
|
8
|
+
}
|
|
9
|
+
function createTestWorkflowFactory(creator) {
|
|
10
|
+
return creator;
|
|
11
|
+
}
|