@recombine-ai/engine 0.11.3 → 1.0.0-beta-1
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/index.js +1 -0
- package/build/lib/ai.d.ts +14 -60
- package/build/lib/ai.d.ts.map +1 -1
- package/build/lib/ai.js +21 -76
- package/build/lib/bosun/agent.d.ts +4 -6
- package/build/lib/bosun/agent.d.ts.map +1 -1
- package/build/lib/bosun/{apiCallTracer.d.ts → api-call-tracer.d.ts} +1 -1
- package/build/lib/bosun/api-call-tracer.d.ts.map +1 -0
- package/build/lib/bosun/context.d.ts.map +1 -1
- package/build/lib/bosun/context.js +0 -2
- package/build/lib/bosun/{conversationalTracer.d.ts → conversational-tracer.d.ts} +4 -2
- package/build/lib/bosun/conversational-tracer.d.ts.map +1 -0
- package/build/lib/bosun/index.d.ts +4 -4
- package/build/lib/bosun/index.d.ts.map +1 -1
- package/build/lib/bosun/index.js +3 -3
- package/build/lib/bosun/{tracer.d.ts → step-registry.d.ts} +4 -16
- package/build/lib/bosun/step-registry.d.ts.map +1 -0
- package/build/lib/bosun/{tracer.js → step-registry.js} +0 -5
- package/build/lib/bosun/{stepTracer.d.ts → step-tracer.d.ts} +3 -3
- package/build/lib/bosun/step-tracer.d.ts.map +1 -0
- package/build/lib/bosun/{stepTracer.js → step-tracer.js} +2 -5
- package/build/lib/llm-adapters/index.d.ts +2 -0
- package/build/lib/llm-adapters/index.d.ts.map +1 -1
- package/build/lib/llm-adapters/index.js +4 -0
- package/build/lib/llm-adapters/mock.d.ts +3 -0
- package/build/lib/llm-adapters/mock.d.ts.map +1 -0
- package/build/lib/llm-adapters/mock.js +14 -0
- package/build/lib/llm-adapters/openai-stream.d.ts +6 -0
- package/build/lib/llm-adapters/openai-stream.d.ts.map +1 -0
- package/build/lib/llm-adapters/openai-stream.js +30 -0
- package/build/lib/llm-adapters/openai.d.ts +5 -7
- package/build/lib/llm-adapters/openai.d.ts.map +1 -1
- package/build/lib/llm-adapters/openai.js +3 -19
- package/build/lib/stream/ai.d.ts +3 -0
- package/build/lib/stream/ai.d.ts.map +1 -0
- package/build/lib/stream/ai.js +245 -0
- package/build/lib/stream/filter.d.ts +6 -0
- package/build/lib/stream/filter.d.ts.map +1 -0
- package/build/lib/stream/filter.js +20 -0
- package/build/lib/stream/get-client.d.ts +15 -0
- package/build/lib/stream/get-client.d.ts.map +1 -0
- package/build/lib/stream/get-client.js +166 -0
- package/build/lib/stream/index.d.ts +4 -0
- package/build/lib/stream/index.d.ts.map +1 -0
- package/build/lib/stream/index.js +8 -0
- package/build/lib/stream/interfaces.d.ts +73 -0
- package/build/lib/stream/interfaces.d.ts.map +1 -0
- package/build/lib/stream/interfaces.js +2 -0
- package/changelog.md +17 -0
- package/package.json +3 -3
- package/build/lib/bosun/apiCallTracer.d.ts.map +0 -1
- package/build/lib/bosun/conversationalTracer.d.ts.map +0 -1
- package/build/lib/bosun/stepTracer.d.ts.map +0 -1
- package/build/lib/bosun/tracer.d.ts.map +0 -1
- /package/build/lib/bosun/{apiCallTracer.js → api-call-tracer.js} +0 -0
- /package/build/lib/bosun/{conversationalTracer.js → conversational-tracer.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAEvC,wBAAgB,iBAAiB,IAAI,UAAU,CAU9C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMockAdapter = createMockAdapter;
|
|
4
|
+
function createMockAdapter() {
|
|
5
|
+
return {
|
|
6
|
+
getOptions: () => ({ model: 'mock' }),
|
|
7
|
+
async generateResponse(_systemPrompt, _messages, schema) {
|
|
8
|
+
if (schema) {
|
|
9
|
+
return JSON.stringify({ message: 'canned response', reasons: [] });
|
|
10
|
+
}
|
|
11
|
+
return 'canned response';
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { OpenAI } from 'openai';
|
|
2
|
+
import { ChatCompletionCreateParamsBase } from 'openai/resources/chat/completions';
|
|
3
|
+
import { LlmStreamAdapter } from '../stream/interfaces';
|
|
4
|
+
export type OpenAIChatOptions = Omit<ChatCompletionCreateParamsBase, 'messages' | 'response_format' | 'stream'>;
|
|
5
|
+
export declare function createOpenAIStreamAdapter(options: OpenAIChatOptions, client?: OpenAI): LlmStreamAdapter;
|
|
6
|
+
//# sourceMappingURL=openai-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-stream.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/openai-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAA;AAElF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAEvD,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAChC,8BAA8B,EAC9B,UAAU,GAAG,iBAAiB,GAAG,QAAQ,CAC5C,CAAA;AAED,wBAAgB,yBAAyB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,MAAM,SAAe,GACtB,gBAAgB,CA4BlB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOpenAIStreamAdapter = createOpenAIStreamAdapter;
|
|
4
|
+
const openai_1 = require("openai");
|
|
5
|
+
function createOpenAIStreamAdapter(options, client = new openai_1.OpenAI()) {
|
|
6
|
+
return {
|
|
7
|
+
getOptions: () => options,
|
|
8
|
+
async generateStream(systemPrompt, messages) {
|
|
9
|
+
const stream = await client.chat.completions.create({
|
|
10
|
+
messages: [
|
|
11
|
+
{ role: 'system', content: systemPrompt },
|
|
12
|
+
{ role: 'user', content: messages },
|
|
13
|
+
],
|
|
14
|
+
...options,
|
|
15
|
+
stream: true,
|
|
16
|
+
response_format: { type: 'text' },
|
|
17
|
+
});
|
|
18
|
+
return new ReadableStream({
|
|
19
|
+
async start(controller) {
|
|
20
|
+
for await (const chunk of stream) {
|
|
21
|
+
const content = chunk.choices?.[0]?.delta?.content;
|
|
22
|
+
if (content)
|
|
23
|
+
controller.enqueue(content);
|
|
24
|
+
}
|
|
25
|
+
controller.close();
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { OpenAI } from 'openai';
|
|
1
2
|
import { ChatCompletionCreateParamsBase } from 'openai/resources/chat/completions';
|
|
2
3
|
import type { LlmAdapter } from '../ai';
|
|
3
|
-
|
|
4
|
-
export type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
};
|
|
9
|
-
export declare function createOpenAIAdapter(options: OpenAIChatOptions, auth: OpenAIAdapterAuth): LlmAdapter;
|
|
4
|
+
type OpenaiOptionsToSend = Omit<ChatCompletionCreateParamsBase, 'messages' | 'stream'>;
|
|
5
|
+
export type OpenAIChatOptions = Omit<OpenaiOptionsToSend, 'response_format'>;
|
|
6
|
+
export declare function createOpenAIAdapter(options: OpenAIChatOptions, client?: OpenAI): LlmAdapter;
|
|
7
|
+
export {};
|
|
10
8
|
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/openai.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/lib/llm-adapters/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAA;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAGvC,KAAK,mBAAmB,GAAG,IAAI,CAAC,8BAA8B,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAA;AACtF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,mBAAmB,EAAE,iBAAiB,CAAC,CAAA;AAE5E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAe,GAAG,UAAU,CAmCjG"}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.createOpenAIAdapter = createOpenAIAdapter;
|
|
7
4
|
const openai_1 = require("openai");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
function createOpenAIAdapter(options, auth) {
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
function createOpenAIAdapter(options, client = new openai_1.OpenAI()) {
|
|
11
7
|
return {
|
|
12
8
|
getOptions: () => options,
|
|
13
9
|
async generateResponse(systemPrompt, messages, schema) {
|
|
@@ -17,23 +13,11 @@ function createOpenAIAdapter(options, auth) {
|
|
|
17
13
|
type: 'json_schema',
|
|
18
14
|
json_schema: {
|
|
19
15
|
name: 'detector_response',
|
|
20
|
-
schema: (0,
|
|
16
|
+
schema: (0, zod_1.toJSONSchema)(schema),
|
|
21
17
|
strict: true,
|
|
22
18
|
},
|
|
23
19
|
};
|
|
24
20
|
}
|
|
25
|
-
const apiKey = await auth.tokenStorage.getToken();
|
|
26
|
-
if (!apiKey) {
|
|
27
|
-
throw new Error('OpenAI API key is not set');
|
|
28
|
-
}
|
|
29
|
-
if (apiKey === '__TESTING__') {
|
|
30
|
-
await delay(100);
|
|
31
|
-
if (options.response_format && 'json_schema' in options.response_format) {
|
|
32
|
-
return JSON.stringify({ message: 'canned response', reasons: [] });
|
|
33
|
-
}
|
|
34
|
-
return 'canned response';
|
|
35
|
-
}
|
|
36
|
-
const client = new openai_1.OpenAI({ apiKey });
|
|
37
21
|
const response = await client.chat.completions.create({
|
|
38
22
|
messages: [
|
|
39
23
|
{ role: 'system', content: systemPrompt },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../../src/lib/stream/ai.ts"],"names":[],"mappings":"AAOA,OAAO,EACH,cAAc,EAGd,qBAAqB,EAGxB,MAAM,cAAc,CAAA;AAIrB,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,GAAG,cAAc,CA6N/E"}
|
|
@@ -0,0 +1,245 @@
|
|
|
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.createAIStreamEngine = createAIStreamEngine;
|
|
7
|
+
// cspell:words TTFT lstrip
|
|
8
|
+
const nunjucks_1 = __importDefault(require("nunjucks"));
|
|
9
|
+
const bosun_1 = require("../bosun");
|
|
10
|
+
const filter_1 = require("./filter");
|
|
11
|
+
const step_registry_1 = require("../bosun/step-registry");
|
|
12
|
+
function createAIStreamEngine(cfg) {
|
|
13
|
+
function createWorkflow({ name, prompt, model, filter = filter_1.defaultFilter, onError, }) {
|
|
14
|
+
const logger = cfg.logger || console;
|
|
15
|
+
logger.debug('streamAiEngine.createWorkflow');
|
|
16
|
+
const stepTracer = cfg.stepTracer || (0, bosun_1.createStubStepTracer)(logger);
|
|
17
|
+
cfg.stepRegistry?.addStep({
|
|
18
|
+
type: 'streaming-response',
|
|
19
|
+
name,
|
|
20
|
+
prompt: (0, step_registry_1.stdPrompt)(prompt),
|
|
21
|
+
});
|
|
22
|
+
async function run(messages, ctx) {
|
|
23
|
+
let streamCancelled = false;
|
|
24
|
+
return new ReadableStream({
|
|
25
|
+
cancel: (reason) => {
|
|
26
|
+
logger.debug(`streamingWorkflow.run cancelled: ${reason}`);
|
|
27
|
+
streamCancelled = true;
|
|
28
|
+
},
|
|
29
|
+
start: async (controller) => {
|
|
30
|
+
try {
|
|
31
|
+
const stream = await generateResponseStream(messages, ctx);
|
|
32
|
+
for await (const chunk of stream) {
|
|
33
|
+
if (streamCancelled)
|
|
34
|
+
return;
|
|
35
|
+
controller.enqueue(chunk);
|
|
36
|
+
}
|
|
37
|
+
if (streamCancelled)
|
|
38
|
+
return;
|
|
39
|
+
controller.close();
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
if (streamCancelled) {
|
|
43
|
+
logger.debug('streamingWorkflow.run will not propagate error due to stream cancellation', e);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
controller.error(e);
|
|
47
|
+
await onError(e, ctx);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function generateResponseStream(messages, ctx) {
|
|
53
|
+
const runId = crypto.randomUUID();
|
|
54
|
+
logger.debug(`streamingWorkflow.run runId: ${runId}`);
|
|
55
|
+
const startTime = performance.now();
|
|
56
|
+
const transcript = createTranscript(logger, messages);
|
|
57
|
+
let currentFilter = null;
|
|
58
|
+
let filteredTokens = [];
|
|
59
|
+
// Create step trace for telescope
|
|
60
|
+
const mainStepTrace = {
|
|
61
|
+
workflowId: name,
|
|
62
|
+
workflowRunId: runId,
|
|
63
|
+
name: name,
|
|
64
|
+
model: String(model.getOptions()),
|
|
65
|
+
createdAt: Date.now(),
|
|
66
|
+
response: '',
|
|
67
|
+
};
|
|
68
|
+
const renderedPrompt = await renderPrompt(prompt, ctx);
|
|
69
|
+
const stringifiedConversation = transcript.toString();
|
|
70
|
+
mainStepTrace.renderedPrompt = renderedPrompt;
|
|
71
|
+
mainStepTrace.stringifiedConversation = stringifiedConversation;
|
|
72
|
+
let mainStepStream;
|
|
73
|
+
try {
|
|
74
|
+
mainStepStream = await model.generateStream(renderedPrompt, stringifiedConversation);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
logger.error('AI Engine Stream: LLM error', { error });
|
|
78
|
+
if (mainStepTrace) {
|
|
79
|
+
mainStepTrace.error = error instanceof Error ? error : new Error(String(error));
|
|
80
|
+
cfg.stepTracer?.addStepTrace(mainStepTrace);
|
|
81
|
+
await cfg.stepTracer?.flush();
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
logger.debug(`[MARK] stream created: ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
86
|
+
let measureTtft = true;
|
|
87
|
+
let streamCancelled = false;
|
|
88
|
+
return new ReadableStream({
|
|
89
|
+
cancel: (reason) => {
|
|
90
|
+
logger.debug(`streamingWorkflow.generateResponseStream cancelled: ${reason}`);
|
|
91
|
+
streamCancelled = true;
|
|
92
|
+
},
|
|
93
|
+
async start(controller) {
|
|
94
|
+
logger.debug('streamingWorkflow.run: starting main stream');
|
|
95
|
+
for await (const chunk of mainStepStream) {
|
|
96
|
+
if (measureTtft) {
|
|
97
|
+
logger.debug(`[MARK] TTFT: ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
98
|
+
measureTtft = false;
|
|
99
|
+
}
|
|
100
|
+
const delta = chunk;
|
|
101
|
+
if (!delta) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
let tokensToRelease = [];
|
|
105
|
+
if (currentFilter) {
|
|
106
|
+
filteredTokens.push(delta);
|
|
107
|
+
const filterResult = currentFilter.onNewToken(transcript, filteredTokens);
|
|
108
|
+
if (filterResult.action === 'RELEASE_TOKENS') {
|
|
109
|
+
logger.debug('streamingWorkflow.run: programmatic filter releasing tokens: ', JSON.stringify(filterResult.tokens));
|
|
110
|
+
tokensToRelease = filterResult.tokens;
|
|
111
|
+
currentFilter = null;
|
|
112
|
+
filteredTokens = [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (filter?.shouldStartFiltering(transcript, delta)) {
|
|
116
|
+
filteredTokens = [delta];
|
|
117
|
+
currentFilter = filter;
|
|
118
|
+
logger.debug('streamingWorkflow.run: programmatic filter is applied on token: ', delta);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
tokensToRelease = [delta];
|
|
122
|
+
}
|
|
123
|
+
releaseMainStreamTokens(tokensToRelease);
|
|
124
|
+
}
|
|
125
|
+
if (currentFilter) {
|
|
126
|
+
// stream has ended, but the filter still has some tokens to release
|
|
127
|
+
releaseMainStreamTokens(currentFilter.onStreamEnd(transcript, filteredTokens).tokensToRelease);
|
|
128
|
+
currentFilter = null;
|
|
129
|
+
filteredTokens = [];
|
|
130
|
+
}
|
|
131
|
+
function releaseMainStreamTokens(tokens) {
|
|
132
|
+
for (const token of tokens) {
|
|
133
|
+
if (streamCancelled) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
transcript.responseChunks.push({ role: 'agent', delta: token });
|
|
137
|
+
controller.enqueue({ role: 'agent', delta: token });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
logger.debug('streamingWorkflow.run: markMainResponseFinished', {
|
|
141
|
+
elapsedMs: (performance.now() - startTime).toFixed(2),
|
|
142
|
+
});
|
|
143
|
+
transcript.markMainResponseFinished();
|
|
144
|
+
// Add step trace and flush to telescope
|
|
145
|
+
cfg.stepTracer?.addStepTrace(mainStepTrace);
|
|
146
|
+
await cfg.stepTracer?.flush();
|
|
147
|
+
if (cfg.conversationalTracer) {
|
|
148
|
+
try {
|
|
149
|
+
const ctxWithState = ctx;
|
|
150
|
+
const rawConversationId = ctxWithState?.state?.callId ?? ctxWithState?.callId;
|
|
151
|
+
const conversationId = typeof rawConversationId === 'string' && rawConversationId !== ''
|
|
152
|
+
? rawConversationId
|
|
153
|
+
: undefined;
|
|
154
|
+
cfg.conversationalTracer.addConversationalTrace({
|
|
155
|
+
conversationId,
|
|
156
|
+
eventName: 'finished-main-streaming-step',
|
|
157
|
+
role: 'agent',
|
|
158
|
+
medium: 'phone',
|
|
159
|
+
content: transcript.currentResponse,
|
|
160
|
+
createdAt: Date.now(),
|
|
161
|
+
});
|
|
162
|
+
await cfg.conversationalTracer.flush();
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
logger.error('Failed to write conversational trace', { err });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!streamCancelled) {
|
|
169
|
+
controller.close();
|
|
170
|
+
}
|
|
171
|
+
await stepTracer.flush();
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function renderPrompt(prompt, context) {
|
|
176
|
+
logger.debug('Streaming AI, CONTEXT:', context);
|
|
177
|
+
const template = typeof prompt === 'string' ? prompt : await prompt.content();
|
|
178
|
+
if (context) {
|
|
179
|
+
logger.debug('Loaded context: ', context);
|
|
180
|
+
nunjucks_1.default.configure({
|
|
181
|
+
autoescape: true,
|
|
182
|
+
trimBlocks: true,
|
|
183
|
+
lstripBlocks: true,
|
|
184
|
+
});
|
|
185
|
+
return nunjucks_1.default.renderString(template, context);
|
|
186
|
+
}
|
|
187
|
+
return template;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
run,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
createWorkflow,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function createTranscript(logger, initialMessages) {
|
|
198
|
+
let finished = false;
|
|
199
|
+
const messages = [...initialMessages];
|
|
200
|
+
const directivesFormatter = (msg) => `${msg.sender}: ${msg.text}`;
|
|
201
|
+
const currentResponseFormatter = (partial) => `Current response: ${partial}`;
|
|
202
|
+
const responseTokens = [];
|
|
203
|
+
const names = {
|
|
204
|
+
agent: 'Agent',
|
|
205
|
+
user: 'User',
|
|
206
|
+
system: 'System',
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
responseChunks: responseTokens,
|
|
210
|
+
get currentResponse() {
|
|
211
|
+
return responseTokens
|
|
212
|
+
.filter((token) => token.role === 'agent')
|
|
213
|
+
.map((token) => token?.delta)
|
|
214
|
+
.join('');
|
|
215
|
+
},
|
|
216
|
+
get mainResponseFinished() {
|
|
217
|
+
return finished;
|
|
218
|
+
},
|
|
219
|
+
get messages() {
|
|
220
|
+
return initialMessages;
|
|
221
|
+
},
|
|
222
|
+
markMainResponseFinished() {
|
|
223
|
+
logger.debug('Conversation, markMainResponseFinished');
|
|
224
|
+
finished = true;
|
|
225
|
+
},
|
|
226
|
+
toString(ignoreDirectives = false) {
|
|
227
|
+
const serializedMessages = messages
|
|
228
|
+
.map((msg) => {
|
|
229
|
+
if (msg.sender === 'system') {
|
|
230
|
+
return ignoreDirectives ? null : directivesFormatter(msg);
|
|
231
|
+
}
|
|
232
|
+
return `${names[msg.sender]}: ${msg.text}`;
|
|
233
|
+
})
|
|
234
|
+
.filter((line) => line !== null)
|
|
235
|
+
.join('\n');
|
|
236
|
+
if (responseTokens.length === 0) {
|
|
237
|
+
return serializedMessages;
|
|
238
|
+
}
|
|
239
|
+
return serializedMessages + '\n' + currentResponseFormatter(this.currentResponse);
|
|
240
|
+
},
|
|
241
|
+
getConversation() {
|
|
242
|
+
return messages;
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../../src/lib/stream/filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEjD;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,kBAc3B,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultFilter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* filter out 'Agent:' at the beginning of the llm response
|
|
6
|
+
*/
|
|
7
|
+
exports.defaultFilter = {
|
|
8
|
+
shouldStartFiltering(state, newToken) {
|
|
9
|
+
return state.currentResponse.trim().length === 0 && newToken.toLowerCase() === 'agent';
|
|
10
|
+
},
|
|
11
|
+
onNewToken(_state, filteredTokens) {
|
|
12
|
+
if (filteredTokens.length >= 2 && filteredTokens[1] === ':') {
|
|
13
|
+
return { action: 'RELEASE_TOKENS', tokens: filteredTokens.slice(2) };
|
|
14
|
+
}
|
|
15
|
+
return { action: 'RELEASE_TOKENS', tokens: filteredTokens };
|
|
16
|
+
},
|
|
17
|
+
onStreamEnd(_state, filteredTokens) {
|
|
18
|
+
return { tokensToRelease: filteredTokens };
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import OpenAI, { AzureOpenAI } from 'openai';
|
|
2
|
+
import { Logger } from '../interfaces';
|
|
3
|
+
interface AzureConfig {
|
|
4
|
+
endpoint: string;
|
|
5
|
+
apiVersion: string;
|
|
6
|
+
deployment: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
}
|
|
9
|
+
interface OpenAiConfig {
|
|
10
|
+
apiKey: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function getAzureClient(logger: Logger, config: AzureConfig): AzureOpenAI;
|
|
13
|
+
export declare function getOpenAiClient(logger: Logger, config: OpenAiConfig): OpenAI;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=get-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-client.d.ts","sourceRoot":"","sources":["../../../src/lib/stream/get-client.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtC,UAAU,WAAW;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,YAAY;IAClB,MAAM,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,eASjE;AACD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,UAMnE"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getAzureClient = getAzureClient;
|
|
37
|
+
exports.getOpenAiClient = getOpenAiClient;
|
|
38
|
+
// cspell:words Nagle's
|
|
39
|
+
const openai_1 = __importStar(require("openai"));
|
|
40
|
+
const undici_1 = require("undici");
|
|
41
|
+
function getAzureClient(logger, config) {
|
|
42
|
+
logger.debug(`getting azure client: ${config.endpoint}, v: ${config.apiVersion}`);
|
|
43
|
+
return new openai_1.AzureOpenAI({
|
|
44
|
+
apiKey: config.apiKey,
|
|
45
|
+
apiVersion: config.apiVersion,
|
|
46
|
+
endpoint: config.endpoint,
|
|
47
|
+
deployment: config.deployment,
|
|
48
|
+
fetch: getOptimizedFetch(logger, new URL(config.endpoint).origin),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function getOpenAiClient(logger, config) {
|
|
52
|
+
logger.debug(`getting OpenAi client.`);
|
|
53
|
+
return new openai_1.default({
|
|
54
|
+
apiKey: config.apiKey,
|
|
55
|
+
fetch: getOptimizedFetch(logger, 'https://api.openai.com'),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function getOptimizedFetch(logger, url) {
|
|
59
|
+
const defaultHeaders = {
|
|
60
|
+
'content-type': 'application/json',
|
|
61
|
+
accept: 'application/json',
|
|
62
|
+
connection: 'keep-alive',
|
|
63
|
+
};
|
|
64
|
+
const httpClient = new undici_1.Client(url, {
|
|
65
|
+
// Keep connections alive for reuse
|
|
66
|
+
keepAliveTimeout: 120_000,
|
|
67
|
+
keepAliveMaxTimeout: 300_000,
|
|
68
|
+
// HTTP/1.1 pipelining for multiple requests
|
|
69
|
+
pipelining: 10,
|
|
70
|
+
// TCP socket optimizations
|
|
71
|
+
connect: {
|
|
72
|
+
// Disable Nagle's algorithm for lower latency
|
|
73
|
+
noDelay: true,
|
|
74
|
+
// Keep socket alive
|
|
75
|
+
keepAlive: true,
|
|
76
|
+
// Skip SSL verification for trusted API endpoints
|
|
77
|
+
rejectUnauthorized: false,
|
|
78
|
+
},
|
|
79
|
+
// DNS caching
|
|
80
|
+
maxCachedSessions: 100,
|
|
81
|
+
// Timeout optimizations
|
|
82
|
+
headersTimeout: 30000,
|
|
83
|
+
bodyTimeout: 60000,
|
|
84
|
+
});
|
|
85
|
+
process.on('SIGTERM', () => {
|
|
86
|
+
void httpClient.close();
|
|
87
|
+
});
|
|
88
|
+
process.on('SIGINT', () => {
|
|
89
|
+
void httpClient.close();
|
|
90
|
+
});
|
|
91
|
+
async function customFetch(url, options = {}) {
|
|
92
|
+
const requestUrl = url instanceof Request ? url.url : url.toString();
|
|
93
|
+
const method = options.method || 'GET';
|
|
94
|
+
logger.debug('Optimized Fetch', { method, url });
|
|
95
|
+
const originalHeaders = options.headers instanceof Headers
|
|
96
|
+
? Object.fromEntries(options.headers.entries())
|
|
97
|
+
: options.headers;
|
|
98
|
+
const headers = {
|
|
99
|
+
...defaultHeaders,
|
|
100
|
+
...(originalHeaders || {}),
|
|
101
|
+
};
|
|
102
|
+
const response = await httpClient.request({
|
|
103
|
+
path: new URL(requestUrl).pathname + new URL(requestUrl).search,
|
|
104
|
+
method,
|
|
105
|
+
headers,
|
|
106
|
+
body: options.body,
|
|
107
|
+
origin: new URL(requestUrl).origin,
|
|
108
|
+
});
|
|
109
|
+
// Convert undici headers to standard Headers
|
|
110
|
+
const responseHeaders = new Headers();
|
|
111
|
+
Object.entries(response.headers).forEach(([key, value]) => {
|
|
112
|
+
if (Array.isArray(value)) {
|
|
113
|
+
value.forEach((v) => responseHeaders.append(key, v));
|
|
114
|
+
}
|
|
115
|
+
else if (value !== undefined) {
|
|
116
|
+
responseHeaders.append(key, value);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
if (response.statusCode === 429) {
|
|
120
|
+
console.warn('Quota limit exceeded.');
|
|
121
|
+
}
|
|
122
|
+
// Create a proper Response with streaming support
|
|
123
|
+
let canceled = false;
|
|
124
|
+
return new Response(new ReadableStream({
|
|
125
|
+
cancel() {
|
|
126
|
+
canceled = true;
|
|
127
|
+
},
|
|
128
|
+
start(controller) {
|
|
129
|
+
response.body.on('data', (chunk) => {
|
|
130
|
+
if (canceled)
|
|
131
|
+
return;
|
|
132
|
+
try {
|
|
133
|
+
controller.enqueue(chunk);
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
logger.error(e);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
response.body.on('end', () => {
|
|
140
|
+
if (canceled)
|
|
141
|
+
return;
|
|
142
|
+
try {
|
|
143
|
+
controller.close();
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
logger.error(e);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
response.body.on('error', (err) => {
|
|
150
|
+
if (canceled)
|
|
151
|
+
return;
|
|
152
|
+
try {
|
|
153
|
+
controller.error(err);
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
logger.error(e);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
}), {
|
|
161
|
+
status: response.statusCode,
|
|
162
|
+
headers: responseHeaders,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return customFetch;
|
|
166
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createAIStreamEngine } from './ai';
|
|
2
|
+
export { AIStreamEngine, ProgrammaticFilter, LlmStreamAdapter, ResponseChunk, Transcript, StreamingEngineConfig, WorkflowConfig as StreamingWorkflowConfig, } from './interfaces';
|
|
3
|
+
export { getAzureClient, getOpenAiClient } from './get-client';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/stream/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,MAAM,CAAA;AAE3C,OAAO,EACH,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,qBAAqB,EACrB,cAAc,IAAI,uBAAuB,GAC5C,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getOpenAiClient = exports.getAzureClient = exports.createAIStreamEngine = void 0;
|
|
4
|
+
var ai_1 = require("./ai");
|
|
5
|
+
Object.defineProperty(exports, "createAIStreamEngine", { enumerable: true, get: function () { return ai_1.createAIStreamEngine; } });
|
|
6
|
+
var get_client_1 = require("./get-client");
|
|
7
|
+
Object.defineProperty(exports, "getAzureClient", { enumerable: true, get: function () { return get_client_1.getAzureClient; } });
|
|
8
|
+
Object.defineProperty(exports, "getOpenAiClient", { enumerable: true, get: function () { return get_client_1.getOpenAiClient; } });
|