@proteinjs/conversation 1.5.2 → 1.6.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/CHANGELOG.md +22 -0
- package/dist/src/Conversation.d.ts +2 -0
- package/dist/src/Conversation.d.ts.map +1 -1
- package/dist/src/Conversation.js +13 -0
- package/dist/src/Conversation.js.map +1 -1
- package/dist/src/OpenAi.d.ts +9 -1
- package/dist/src/OpenAi.d.ts.map +1 -1
- package/dist/src/OpenAi.js +158 -78
- package/dist/src/OpenAi.js.map +1 -1
- package/dist/src/OpenAiStreamProcessor.d.ts +45 -0
- package/dist/src/OpenAiStreamProcessor.d.ts.map +1 -0
- package/dist/src/OpenAiStreamProcessor.js +202 -0
- package/dist/src/OpenAiStreamProcessor.js.map +1 -0
- package/package.json +2 -2
- package/src/Conversation.ts +12 -0
- package/src/OpenAi.ts +224 -76
- package/src/OpenAiStreamProcessor.ts +155 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.OpenAiStreamProcessor = void 0;
|
|
40
|
+
var util_1 = require("@proteinjs/util");
|
|
41
|
+
var stream_1 = require("stream");
|
|
42
|
+
/**
|
|
43
|
+
* Processes streaming responses from OpenAI's `ChatCompletions` api.
|
|
44
|
+
* - When a tool call is received, it delegates processing to `onToolCalls`; this can happen recursively
|
|
45
|
+
* - When a response to the user is received, it writes to `outputStream`
|
|
46
|
+
*/
|
|
47
|
+
var OpenAiStreamProcessor = /** @class */ (function () {
|
|
48
|
+
function OpenAiStreamProcessor(inputStream, onToolCalls, logLevel) {
|
|
49
|
+
this.onToolCalls = onToolCalls;
|
|
50
|
+
this.accumulatedToolCalls = [];
|
|
51
|
+
this.toolCallsExecuted = 0;
|
|
52
|
+
this.currentToolCall = null;
|
|
53
|
+
this.logger = new util_1.Logger(this.constructor.name, logLevel);
|
|
54
|
+
this.inputStream = stream_1.Readable.from(inputStream);
|
|
55
|
+
this.controlStream = this.createControlStream();
|
|
56
|
+
this.outputStream = new stream_1.PassThrough();
|
|
57
|
+
this.inputStream.pipe(this.controlStream);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @returns a `Readable` stream that will receive the assistant's text response to the user
|
|
61
|
+
*/
|
|
62
|
+
OpenAiStreamProcessor.prototype.getOutputStream = function () {
|
|
63
|
+
return this.outputStream;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* @returns a `Transform` that parses the input stream and delegates to tool calls or writes a text response to the user
|
|
67
|
+
*/
|
|
68
|
+
OpenAiStreamProcessor.prototype.createControlStream = function () {
|
|
69
|
+
var _this = this;
|
|
70
|
+
return new stream_1.Transform({
|
|
71
|
+
objectMode: true,
|
|
72
|
+
transform: function (chunk, encoding, callback) {
|
|
73
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
74
|
+
try {
|
|
75
|
+
if (_this.outputStream.destroyed) {
|
|
76
|
+
_this.logger.warn("Destroying input and control streams since output stream is destroyed");
|
|
77
|
+
_this.inputStream.destroy();
|
|
78
|
+
_this.controlStream.destroy();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!chunk || !chunk.choices) {
|
|
82
|
+
throw new Error("Received invalid chunk:\n".concat(JSON.stringify(chunk, null, 2)));
|
|
83
|
+
}
|
|
84
|
+
else if ((_b = (_a = chunk.choices[0]) === null || _a === void 0 ? void 0 : _a.delta) === null || _b === void 0 ? void 0 : _b.content) {
|
|
85
|
+
_this.outputStream.push(chunk.choices[0].delta.content);
|
|
86
|
+
}
|
|
87
|
+
else if ((_d = (_c = chunk.choices[0]) === null || _c === void 0 ? void 0 : _c.delta) === null || _d === void 0 ? void 0 : _d.tool_calls) {
|
|
88
|
+
_this.handleToolCallDelta(chunk.choices[0].delta.tool_calls);
|
|
89
|
+
}
|
|
90
|
+
else if (((_e = chunk.choices[0]) === null || _e === void 0 ? void 0 : _e.finish_reason) === 'tool_calls') {
|
|
91
|
+
_this.handleToolCalls();
|
|
92
|
+
}
|
|
93
|
+
else if (((_f = chunk.choices[0]) === null || _f === void 0 ? void 0 : _f.finish_reason) === 'stop') {
|
|
94
|
+
_this.outputStream.push(null);
|
|
95
|
+
}
|
|
96
|
+
else if (((_g = chunk.choices[0]) === null || _g === void 0 ? void 0 : _g.finish_reason) === 'length') {
|
|
97
|
+
_this.logger.warn("The maximum number of tokens specified in the request was reached");
|
|
98
|
+
_this.outputStream.push(null);
|
|
99
|
+
}
|
|
100
|
+
else if (((_h = chunk.choices[0]) === null || _h === void 0 ? void 0 : _h.finish_reason) === 'content_filter') {
|
|
101
|
+
_this.logger.error("Content was omitted due to a flag from OpenAI's content filters");
|
|
102
|
+
_this.outputStream.push(null);
|
|
103
|
+
}
|
|
104
|
+
callback();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
_this.logger.error('Error tranforming chunk', error);
|
|
108
|
+
_this.destroyStreams(error);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Destroy all streams used by `OpenAiStreamProcessor`
|
|
115
|
+
*/
|
|
116
|
+
OpenAiStreamProcessor.prototype.destroyStreams = function (error) {
|
|
117
|
+
this.inputStream.destroy();
|
|
118
|
+
this.controlStream.destroy();
|
|
119
|
+
this.outputStream.emit('error', error);
|
|
120
|
+
this.outputStream.destroy();
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Accumulates tool call deltas into complete tool calls.
|
|
124
|
+
* @param toolCallDeltas `ChatCompletionChunk.Choice.Delta.ToolCall` objects that contain part of a complete `ChatCompletionMessageToolCall`
|
|
125
|
+
*/
|
|
126
|
+
OpenAiStreamProcessor.prototype.handleToolCallDelta = function (toolCallDeltas) {
|
|
127
|
+
var _a, _b, _c, _d;
|
|
128
|
+
for (var _i = 0, toolCallDeltas_1 = toolCallDeltas; _i < toolCallDeltas_1.length; _i++) {
|
|
129
|
+
var delta = toolCallDeltas_1[_i];
|
|
130
|
+
if (delta.id) {
|
|
131
|
+
// Start of a new tool call
|
|
132
|
+
if (this.currentToolCall) {
|
|
133
|
+
this.accumulatedToolCalls.push(this.currentToolCall);
|
|
134
|
+
}
|
|
135
|
+
this.currentToolCall = {
|
|
136
|
+
id: delta.id,
|
|
137
|
+
type: delta.type || 'function',
|
|
138
|
+
function: {
|
|
139
|
+
name: ((_a = delta.function) === null || _a === void 0 ? void 0 : _a.name) || '',
|
|
140
|
+
arguments: ((_b = delta.function) === null || _b === void 0 ? void 0 : _b.arguments) || '',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Continue building the current tool call
|
|
146
|
+
if ((_c = delta.function) === null || _c === void 0 ? void 0 : _c.name) {
|
|
147
|
+
this.currentToolCall.function.name += delta.function.name;
|
|
148
|
+
}
|
|
149
|
+
if ((_d = delta.function) === null || _d === void 0 ? void 0 : _d.arguments) {
|
|
150
|
+
this.currentToolCall.function.arguments += delta.function.arguments;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Delegates `ChatCompletionMessageToolCall`s to `onToolCalls`.
|
|
157
|
+
* - Manages refreshing the `inputStream` and `controlStream`
|
|
158
|
+
* - Manages tool call state (such as keeping track of the number of tool calls made)
|
|
159
|
+
*/
|
|
160
|
+
OpenAiStreamProcessor.prototype.handleToolCalls = function () {
|
|
161
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
162
|
+
var completedToolCalls, _a, error_1;
|
|
163
|
+
var _this = this;
|
|
164
|
+
return __generator(this, function (_b) {
|
|
165
|
+
switch (_b.label) {
|
|
166
|
+
case 0:
|
|
167
|
+
if (this.currentToolCall) {
|
|
168
|
+
this.accumulatedToolCalls.push(this.currentToolCall);
|
|
169
|
+
this.currentToolCall = null;
|
|
170
|
+
}
|
|
171
|
+
completedToolCalls = this.accumulatedToolCalls.filter(function (tc) {
|
|
172
|
+
return tc.id !== undefined && tc.function !== undefined && tc.type !== undefined;
|
|
173
|
+
});
|
|
174
|
+
this.accumulatedToolCalls = [];
|
|
175
|
+
this.inputStream.destroy();
|
|
176
|
+
this.controlStream.destroy();
|
|
177
|
+
this.controlStream = this.createControlStream();
|
|
178
|
+
_b.label = 1;
|
|
179
|
+
case 1:
|
|
180
|
+
_b.trys.push([1, 3, , 4]);
|
|
181
|
+
_a = this;
|
|
182
|
+
return [4 /*yield*/, this.onToolCalls(completedToolCalls, this.toolCallsExecuted)];
|
|
183
|
+
case 2:
|
|
184
|
+
_a.inputStream = _b.sent();
|
|
185
|
+
this.inputStream.on('error', function (error) { return _this.destroyStreams(error); });
|
|
186
|
+
this.inputStream.pipe(this.controlStream);
|
|
187
|
+
this.toolCallsExecuted += completedToolCalls.length;
|
|
188
|
+
return [3 /*break*/, 4];
|
|
189
|
+
case 3:
|
|
190
|
+
error_1 = _b.sent();
|
|
191
|
+
this.logger.error('Error processing tool calls:', error_1);
|
|
192
|
+
this.destroyStreams(error_1);
|
|
193
|
+
return [3 /*break*/, 4];
|
|
194
|
+
case 4: return [2 /*return*/];
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
return OpenAiStreamProcessor;
|
|
200
|
+
}());
|
|
201
|
+
exports.OpenAiStreamProcessor = OpenAiStreamProcessor;
|
|
202
|
+
//# sourceMappingURL=OpenAiStreamProcessor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAiStreamProcessor.js","sourceRoot":"","sources":["../../src/OpenAiStreamProcessor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,wCAAmD;AAEnD,iCAA6E;AAE7E;;;;GAIG;AACH;IASE,+BACE,WAAwC,EAChC,WAGc,EACtB,QAAkB;QAJV,gBAAW,GAAX,WAAW,CAGG;QAZhB,yBAAoB,GAA6C,EAAE,CAAC;QACpE,sBAAiB,GAAG,CAAC,CAAC;QACtB,oBAAe,GAAkD,IAAI,CAAC;QAa5E,IAAI,CAAC,MAAM,GAAG,IAAI,aAAM,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1D,IAAI,CAAC,WAAW,GAAG,iBAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAChD,IAAI,CAAC,YAAY,GAAG,IAAI,oBAAW,EAAE,CAAC;QACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,+CAAe,GAAf;QACE,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;OAEG;IACK,mDAAmB,GAA3B;QAAA,iBAoCC;QAnCC,OAAO,IAAI,kBAAS,CAAC;YACnB,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,UAAC,KAA0B,EAAE,QAAgB,EAAE,QAA2B;;gBACnF,IAAI;oBACF,IAAI,KAAI,CAAC,YAAY,CAAC,SAAS,EAAE;wBAC/B,KAAI,CAAC,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;wBAC1F,KAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;wBAC3B,KAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;wBAC7B,OAAO;qBACR;oBAED,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;wBAC5B,MAAM,IAAI,KAAK,CAAC,mCAA4B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC;qBAC/E;yBAAM,IAAI,MAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,KAAK,0CAAE,OAAO,EAAE;wBAC3C,KAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;qBACxD;yBAAM,IAAI,MAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,KAAK,0CAAE,UAAU,EAAE;wBAC9C,KAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;qBAC7D;yBAAM,IAAI,CAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,aAAa,MAAK,YAAY,EAAE;wBAC3D,KAAI,CAAC,eAAe,EAAE,CAAC;qBACxB;yBAAM,IAAI,CAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,aAAa,MAAK,MAAM,EAAE;wBACrD,KAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC9B;yBAAM,IAAI,CAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,aAAa,MAAK,QAAQ,EAAE;wBACvD,KAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;wBACtF,KAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC9B;yBAAM,IAAI,CAAA,MAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,0CAAE,aAAa,MAAK,gBAAgB,EAAE;wBAC/D,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iEAAiE,CAAC,CAAC;wBACrF,KAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC9B;oBACD,QAAQ,EAAE,CAAC;iBACZ;gBAAC,OAAO,KAAU,EAAE;oBACnB,KAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;oBACpD,KAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;iBAC5B;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,8CAAc,GAAtB,UAAuB,KAAa;QAClC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACK,mDAAmB,GAA3B,UAA4B,cAA2D;;QACrF,KAAoB,UAAc,EAAd,iCAAc,EAAd,4BAAc,EAAd,IAAc,EAAE;YAA/B,IAAM,KAAK,uBAAA;YACd,IAAI,KAAK,CAAC,EAAE,EAAE;gBACZ,2BAA2B;gBAC3B,IAAI,IAAI,CAAC,eAAe,EAAE;oBACxB,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;iBACtD;gBACD,IAAI,CAAC,eAAe,GAAG;oBACrB,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,UAAU;oBAC9B,QAAQ,EAAE;wBACR,IAAI,EAAE,CAAA,MAAA,KAAK,CAAC,QAAQ,0CAAE,IAAI,KAAI,EAAE;wBAChC,SAAS,EAAE,CAAA,MAAA,KAAK,CAAC,QAAQ,0CAAE,SAAS,KAAI,EAAE;qBAC3C;iBACF,CAAC;aACH;iBAAM;gBACL,0CAA0C;gBAC1C,IAAI,MAAA,KAAK,CAAC,QAAQ,0CAAE,IAAI,EAAE;oBACxB,IAAI,CAAC,eAAgB,CAAC,QAAS,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;iBAC7D;gBACD,IAAI,MAAA,KAAK,CAAC,QAAQ,0CAAE,SAAS,EAAE;oBAC7B,IAAI,CAAC,eAAgB,CAAC,QAAS,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC;iBACvE;aACF;SACF;IACH,CAAC;IAED;;;;OAIG;IACW,+CAAe,GAA7B;;;;;;;wBACE,IAAI,IAAI,CAAC,eAAe,EAAE;4BACxB,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;4BACrD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;yBAC7B;wBAEK,kBAAkB,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CACzD,UAAC,EAAE;4BACD,OAAA,EAAE,CAAC,EAAE,KAAK,SAAS,IAAI,EAAE,CAAC,QAAQ,KAAK,SAAS,IAAI,EAAE,CAAC,IAAI,KAAK,SAAS;wBAAzE,CAAyE,CAC5E,CAAC;wBAEF,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC;wBAC/B,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;wBAC3B,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;wBAC7B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;;;;wBAG9C,KAAA,IAAI,CAAA;wBAAe,qBAAM,IAAI,CAAC,WAAW,CAAC,kBAAkB,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAA;;wBAArF,GAAK,WAAW,GAAG,SAAkE,CAAC;wBACtF,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,UAAC,KAAK,IAAK,OAAA,KAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAA1B,CAA0B,CAAC,CAAC;wBACpE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;wBAC1C,IAAI,CAAC,iBAAiB,IAAI,kBAAkB,CAAC,MAAM,CAAC;;;;wBAEpD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,OAAK,CAAC,CAAC;wBACzD,IAAI,CAAC,cAAc,CAAC,OAAK,CAAC,CAAC;;;;;;KAE9B;IACH,4BAAC;AAAD,CAAC,AAhJD,IAgJC;AAhJY,sDAAqB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/conversation",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"tiktoken": "1.0.15",
|
|
37
37
|
"typescript": "5.2.2"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "e0bc39f8f16e8e9f062194234cee2c4703336da5"
|
|
40
40
|
}
|
package/src/Conversation.ts
CHANGED
|
@@ -209,6 +209,18 @@ export class Conversation {
|
|
|
209
209
|
);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
async generateStreamingResponse(messages: (string | ChatCompletionMessageParam)[], model?: TiktokenModel) {
|
|
213
|
+
await this.enforceTokenLimit(messages, model);
|
|
214
|
+
return await OpenAi.generateStreamingResponse(
|
|
215
|
+
messages,
|
|
216
|
+
model,
|
|
217
|
+
this.history,
|
|
218
|
+
this.functions,
|
|
219
|
+
this.messageModerators,
|
|
220
|
+
this.params.logLevel
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
212
224
|
async generateCode(description: string[], model?: TiktokenModel) {
|
|
213
225
|
this.logger.info(`Generating code for description:\n${description.join('\n')}`);
|
|
214
226
|
const code = await OpenAi.generateCode(
|
package/src/OpenAi.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { OpenAI as OpenAIApi } from 'openai';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ChatCompletionMessageParam,
|
|
4
|
+
ChatCompletion,
|
|
5
|
+
ChatCompletionMessageToolCall,
|
|
6
|
+
ChatCompletionChunk,
|
|
7
|
+
} from 'openai/resources/chat';
|
|
3
8
|
import { LogLevel, Logger, isInstanceOf } from '@proteinjs/util';
|
|
4
9
|
import { MessageModerator } from './history/MessageModerator';
|
|
5
10
|
import { Function } from './Function';
|
|
6
11
|
import { MessageHistory } from './history/MessageHistory';
|
|
7
12
|
import { TiktokenModel } from 'tiktoken';
|
|
8
13
|
import { ChatCompletionMessageParamFactory } from './ChatCompletionMessageParamFactory';
|
|
14
|
+
import { Stream } from 'openai/streaming';
|
|
15
|
+
import { Readable } from 'stream';
|
|
16
|
+
import { OpenAiStreamProcessor } from './OpenAiStreamProcessor';
|
|
9
17
|
|
|
10
18
|
function delay(ms: number) {
|
|
11
19
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -22,8 +30,9 @@ export class OpenAi {
|
|
|
22
30
|
logLevel: LogLevel = 'info',
|
|
23
31
|
maxFunctionCalls: number = 50
|
|
24
32
|
): Promise<string> {
|
|
25
|
-
return await this.generateResponseHelper(
|
|
33
|
+
return (await this.generateResponseHelper(
|
|
26
34
|
messages,
|
|
35
|
+
false,
|
|
27
36
|
0,
|
|
28
37
|
model,
|
|
29
38
|
history,
|
|
@@ -31,11 +40,34 @@ export class OpenAi {
|
|
|
31
40
|
messageModerators,
|
|
32
41
|
logLevel,
|
|
33
42
|
maxFunctionCalls
|
|
34
|
-
);
|
|
43
|
+
)) as string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static async generateStreamingResponse(
|
|
47
|
+
messages: (string | ChatCompletionMessageParam)[],
|
|
48
|
+
model?: string,
|
|
49
|
+
history?: MessageHistory,
|
|
50
|
+
functions?: Omit<Function, 'instructions'>[],
|
|
51
|
+
messageModerators?: MessageModerator[],
|
|
52
|
+
logLevel: LogLevel = 'info',
|
|
53
|
+
maxFunctionCalls: number = 50
|
|
54
|
+
): Promise<Readable> {
|
|
55
|
+
return (await this.generateResponseHelper(
|
|
56
|
+
messages,
|
|
57
|
+
true,
|
|
58
|
+
0,
|
|
59
|
+
model,
|
|
60
|
+
history,
|
|
61
|
+
functions,
|
|
62
|
+
messageModerators,
|
|
63
|
+
logLevel,
|
|
64
|
+
maxFunctionCalls
|
|
65
|
+
)) as Readable;
|
|
35
66
|
}
|
|
36
67
|
|
|
37
68
|
static async generateResponseHelper(
|
|
38
69
|
messages: (string | ChatCompletionMessageParam)[],
|
|
70
|
+
stream: boolean,
|
|
39
71
|
currentFunctionCalls: number,
|
|
40
72
|
model?: string,
|
|
41
73
|
history?: MessageHistory,
|
|
@@ -43,38 +75,44 @@ export class OpenAi {
|
|
|
43
75
|
messageModerators?: MessageModerator[],
|
|
44
76
|
logLevel: LogLevel = 'info',
|
|
45
77
|
maxFunctionCalls: number = 50
|
|
46
|
-
): Promise<string> {
|
|
47
|
-
const logger = new Logger('OpenAi.
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
): Promise<string | Readable> {
|
|
79
|
+
const logger = new Logger('OpenAi.generateResponseHelper', logLevel);
|
|
80
|
+
const updatedHistory = OpenAi.getUpdatedMessageHistory(messages, history, messageModerators);
|
|
81
|
+
const response = await OpenAi.executeRequest(updatedHistory, stream, logLevel, functions, model);
|
|
82
|
+
if (stream) {
|
|
83
|
+
logger.info(`Processing response stream`);
|
|
84
|
+
const inputStream = response as Stream<ChatCompletionChunk>;
|
|
52
85
|
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
history.push(messageParams);
|
|
57
|
-
}
|
|
58
|
-
let messageParamsWithHistory = history ? history : new MessageHistory().push(messageParams);
|
|
59
|
-
if (messageModerators) {
|
|
60
|
-
messageParamsWithHistory = OpenAi.moderateHistory(messageParamsWithHistory, messageModerators);
|
|
61
|
-
}
|
|
62
|
-
const response = await OpenAi.executeRequest(messageParamsWithHistory, logLevel, functions, model);
|
|
63
|
-
const responseMessage = response.choices[0].message;
|
|
64
|
-
if (responseMessage.tool_calls) {
|
|
65
|
-
if (currentFunctionCalls >= maxFunctionCalls) {
|
|
66
|
-
throw new Error(`Max function calls (${maxFunctionCalls}) reached. Stopping execution.`);
|
|
86
|
+
// For subsequent tool calls, return the raw OpenAI stream to `OpenAiStreamProcessor`
|
|
87
|
+
if (currentFunctionCalls > 0) {
|
|
88
|
+
return Readable.from(inputStream);
|
|
67
89
|
}
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
91
|
+
// For the initial call to `generateResponseHelper`, return the `OpenAiStreamProcessor` output stream
|
|
92
|
+
const onToolCalls = ((toolCalls, currentFunctionCalls) =>
|
|
93
|
+
OpenAi.handleToolCalls(
|
|
94
|
+
toolCalls,
|
|
95
|
+
true,
|
|
96
|
+
currentFunctionCalls,
|
|
97
|
+
updatedHistory,
|
|
98
|
+
model,
|
|
99
|
+
functions,
|
|
100
|
+
messageModerators,
|
|
101
|
+
logLevel,
|
|
102
|
+
maxFunctionCalls
|
|
103
|
+
)) as (toolCalls: ChatCompletionMessageToolCall[], currentFunctionCalls: number) => Promise<Readable>;
|
|
104
|
+
const streamProcessor = new OpenAiStreamProcessor(inputStream, onToolCalls, logLevel);
|
|
105
|
+
return streamProcessor.getOutputStream();
|
|
106
|
+
}
|
|
72
107
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
108
|
+
const responseMessage = (response as ChatCompletion).choices[0].message;
|
|
109
|
+
if (responseMessage.tool_calls) {
|
|
110
|
+
return await OpenAi.handleToolCalls(
|
|
111
|
+
responseMessage.tool_calls,
|
|
112
|
+
stream,
|
|
113
|
+
currentFunctionCalls,
|
|
114
|
+
updatedHistory,
|
|
76
115
|
model,
|
|
77
|
-
messageParamsWithHistory,
|
|
78
116
|
functions,
|
|
79
117
|
messageModerators,
|
|
80
118
|
logLevel,
|
|
@@ -87,10 +125,33 @@ export class OpenAi {
|
|
|
87
125
|
throw new Error(`Response was empty for messages: ${messages.join('\n')}`);
|
|
88
126
|
}
|
|
89
127
|
|
|
90
|
-
|
|
128
|
+
updatedHistory.push([responseMessage]);
|
|
91
129
|
return responseText;
|
|
92
130
|
}
|
|
93
131
|
|
|
132
|
+
private static getUpdatedMessageHistory(
|
|
133
|
+
messages: (string | ChatCompletionMessageParam)[],
|
|
134
|
+
history?: MessageHistory,
|
|
135
|
+
messageModerators?: MessageModerator[]
|
|
136
|
+
) {
|
|
137
|
+
const messageParams: ChatCompletionMessageParam[] = messages.map((message) => {
|
|
138
|
+
if (typeof message === 'string') {
|
|
139
|
+
return { role: 'user', content: message };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return message;
|
|
143
|
+
});
|
|
144
|
+
if (history) {
|
|
145
|
+
history.push(messageParams);
|
|
146
|
+
}
|
|
147
|
+
let messageParamsWithHistory = history ? history : new MessageHistory().push(messageParams);
|
|
148
|
+
if (messageModerators) {
|
|
149
|
+
messageParamsWithHistory = OpenAi.moderateHistory(messageParamsWithHistory, messageModerators);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return messageParamsWithHistory;
|
|
153
|
+
}
|
|
154
|
+
|
|
94
155
|
private static moderateHistory(history: MessageHistory, messageModerators: MessageModerator[]) {
|
|
95
156
|
for (const messageModerator of messageModerators) {
|
|
96
157
|
history.setMessages(messageModerator.observe(history.getMessages()));
|
|
@@ -101,24 +162,18 @@ export class OpenAi {
|
|
|
101
162
|
|
|
102
163
|
private static async executeRequest(
|
|
103
164
|
messageParamsWithHistory: MessageHistory,
|
|
165
|
+
stream: boolean,
|
|
104
166
|
logLevel: LogLevel,
|
|
105
167
|
functions?: Omit<Function, 'instructions'>[],
|
|
106
168
|
model?: string
|
|
107
|
-
): Promise<ChatCompletion
|
|
169
|
+
): Promise<ChatCompletion | Stream<ChatCompletionChunk>> {
|
|
108
170
|
const logger = new Logger('OpenAi.executeRequest', logLevel);
|
|
109
171
|
const openaiApi = new OpenAIApi();
|
|
110
|
-
let response: ChatCompletion;
|
|
111
172
|
try {
|
|
112
173
|
const latestMessage = messageParamsWithHistory.getMessages()[messageParamsWithHistory.getMessages().length - 1];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
logger.info(`Sending request: returning output of ${latestMessage.name} function`);
|
|
117
|
-
} else {
|
|
118
|
-
logger.info(`Sending request`);
|
|
119
|
-
}
|
|
120
|
-
logger.debug(`Sending messages: ${JSON.stringify(messageParamsWithHistory.getMessages(), null, 2)}`, true);
|
|
121
|
-
response = await openaiApi.chat.completions.create({
|
|
174
|
+
this.logRequestDetails(logger, logLevel, latestMessage, messageParamsWithHistory);
|
|
175
|
+
|
|
176
|
+
const response = await openaiApi.chat.completions.create({
|
|
122
177
|
model: model ? model : DEFAULT_MODEL,
|
|
123
178
|
temperature: 0,
|
|
124
179
|
messages: messageParamsWithHistory.getMessages(),
|
|
@@ -126,41 +181,130 @@ export class OpenAi {
|
|
|
126
181
|
type: 'function',
|
|
127
182
|
function: f.definition,
|
|
128
183
|
})),
|
|
184
|
+
stream: stream,
|
|
129
185
|
});
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
} else if (responseMessage.tool_calls) {
|
|
134
|
-
logger.info(
|
|
135
|
-
`Received response: call functions: ${JSON.stringify(responseMessage.tool_calls.map((toolCall) => toolCall.function.name))}`
|
|
136
|
-
);
|
|
137
|
-
} else {
|
|
138
|
-
logger.info(`Received response`);
|
|
139
|
-
}
|
|
140
|
-
if (response.usage) {
|
|
141
|
-
logger.info(JSON.stringify(response.usage));
|
|
142
|
-
} else {
|
|
143
|
-
logger.info(JSON.stringify(`Usage data missing`));
|
|
186
|
+
|
|
187
|
+
if (!stream) {
|
|
188
|
+
this.logResponseDetails(logger, response as ChatCompletion);
|
|
144
189
|
}
|
|
190
|
+
|
|
191
|
+
return response;
|
|
145
192
|
} catch (error: any) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
return this.handleRequestError(logger, error, messageParamsWithHistory, stream, logLevel, functions, model);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private static logRequestDetails(
|
|
198
|
+
logger: Logger,
|
|
199
|
+
logLevel: LogLevel,
|
|
200
|
+
latestMessage: ChatCompletionMessageParam,
|
|
201
|
+
messageParamsWithHistory: MessageHistory
|
|
202
|
+
) {
|
|
203
|
+
if (latestMessage.role == 'tool') {
|
|
204
|
+
logger.info(`Sending request: returning output of tool call (${latestMessage.tool_call_id})`);
|
|
205
|
+
} else if (latestMessage.content) {
|
|
206
|
+
const requestContent =
|
|
207
|
+
typeof latestMessage.content === 'string'
|
|
208
|
+
? latestMessage.content
|
|
209
|
+
: latestMessage.content[0].type === 'text'
|
|
210
|
+
? latestMessage.content[0].text
|
|
211
|
+
: 'image';
|
|
212
|
+
logger.info(`Sending request: ${requestContent}`);
|
|
213
|
+
} else {
|
|
214
|
+
logger.info(`Sending request`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (logLevel === 'debug') {
|
|
218
|
+
logger.debug(`Sending messages: ${JSON.stringify(messageParamsWithHistory.getMessages(), null, 2)}`, true);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private static logResponseDetails(logger: Logger, response: ChatCompletion) {
|
|
223
|
+
const responseMessage = response.choices[0].message;
|
|
224
|
+
if (responseMessage.content) {
|
|
225
|
+
logger.info(`Received response: ${responseMessage.content}`);
|
|
226
|
+
} else if (responseMessage.tool_calls) {
|
|
227
|
+
logger.info(
|
|
228
|
+
`Received response: call functions: ${JSON.stringify(responseMessage.tool_calls.map((toolCall) => toolCall.function.name))}`
|
|
229
|
+
);
|
|
230
|
+
} else {
|
|
231
|
+
logger.info(`Received response`);
|
|
232
|
+
}
|
|
233
|
+
if (response.usage) {
|
|
234
|
+
logger.info(JSON.stringify(response.usage));
|
|
235
|
+
} else {
|
|
236
|
+
logger.info(JSON.stringify(`Usage data missing`));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private static async handleRequestError(
|
|
241
|
+
logger: Logger,
|
|
242
|
+
error: any,
|
|
243
|
+
messageParamsWithHistory: MessageHistory,
|
|
244
|
+
stream: boolean,
|
|
245
|
+
logLevel: LogLevel,
|
|
246
|
+
functions?: Omit<Function, 'instructions'>[],
|
|
247
|
+
model?: string
|
|
248
|
+
): Promise<ChatCompletion | Stream<ChatCompletionChunk>> {
|
|
249
|
+
logger.info(`Received error response, error type: ${error.type}`);
|
|
250
|
+
if (typeof error.status !== 'undefined' && error.status == 429) {
|
|
251
|
+
if (error.type == 'tokens' && typeof error.headers['x-ratelimit-reset-tokens'] === 'string') {
|
|
252
|
+
const waitTime = parseInt(error.headers['x-ratelimit-reset-tokens']);
|
|
253
|
+
const remainingTokens = error.headers['x-ratelimit-remaining-tokens'];
|
|
254
|
+
const delayMs = 15000;
|
|
255
|
+
logger.warn(
|
|
256
|
+
`Waiting to retry in ${delayMs / 1000}s, token reset in: ${waitTime}s, remaining tokens: ${remainingTokens}`
|
|
257
|
+
);
|
|
258
|
+
await delay(delayMs);
|
|
259
|
+
return await OpenAi.executeRequest(messageParamsWithHistory, stream, logLevel, functions, model);
|
|
158
260
|
}
|
|
261
|
+
}
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
159
264
|
|
|
160
|
-
|
|
265
|
+
private static async handleToolCalls(
|
|
266
|
+
toolCalls: ChatCompletionMessageToolCall[],
|
|
267
|
+
stream: boolean,
|
|
268
|
+
currentFunctionCalls: number,
|
|
269
|
+
history: MessageHistory,
|
|
270
|
+
model?: string,
|
|
271
|
+
functions?: Omit<Function, 'instructions'>[],
|
|
272
|
+
messageModerators?: MessageModerator[],
|
|
273
|
+
logLevel: LogLevel = 'info',
|
|
274
|
+
maxFunctionCalls: number = 50
|
|
275
|
+
): Promise<string | Readable> {
|
|
276
|
+
if (currentFunctionCalls >= maxFunctionCalls) {
|
|
277
|
+
throw new Error(`Max function calls (${maxFunctionCalls}) reached. Stopping execution.`);
|
|
161
278
|
}
|
|
162
279
|
|
|
163
|
-
|
|
280
|
+
// Create a message for the tool calls
|
|
281
|
+
const toolCallMessage: ChatCompletionMessageParam = {
|
|
282
|
+
role: 'assistant',
|
|
283
|
+
content: null,
|
|
284
|
+
tool_calls: toolCalls,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Add the tool call message to the history
|
|
288
|
+
history.push([toolCallMessage]);
|
|
289
|
+
|
|
290
|
+
// Call the tools and get the responses
|
|
291
|
+
const toolMessageParams = await this.callTools(logLevel, toolCalls, functions);
|
|
292
|
+
|
|
293
|
+
// Add the tool responses to the history
|
|
294
|
+
history.push(toolMessageParams);
|
|
295
|
+
|
|
296
|
+
// Generate the next response
|
|
297
|
+
return this.generateResponseHelper(
|
|
298
|
+
[],
|
|
299
|
+
stream,
|
|
300
|
+
currentFunctionCalls + toolCalls.length,
|
|
301
|
+
model,
|
|
302
|
+
history,
|
|
303
|
+
functions,
|
|
304
|
+
messageModerators,
|
|
305
|
+
logLevel,
|
|
306
|
+
maxFunctionCalls
|
|
307
|
+
);
|
|
164
308
|
}
|
|
165
309
|
|
|
166
310
|
private static async callTools(
|
|
@@ -201,8 +345,12 @@ export class OpenAi {
|
|
|
201
345
|
}
|
|
202
346
|
|
|
203
347
|
try {
|
|
204
|
-
|
|
205
|
-
|
|
348
|
+
const parsedArguments = JSON.parse(functionCall.arguments);
|
|
349
|
+
logger.info(
|
|
350
|
+
`Assistant calling function: (${toolCallId}) ${f.definition.name}(${JSON.stringify(parsedArguments, null, 2)})`,
|
|
351
|
+
1000
|
|
352
|
+
);
|
|
353
|
+
const returnObject = await f.call(parsedArguments);
|
|
206
354
|
|
|
207
355
|
const returnObjectCompletionParams: ChatCompletionMessageParam[] = [];
|
|
208
356
|
if (isInstanceOf(returnObject, ChatCompletionMessageParamFactory)) {
|
|
@@ -216,7 +364,7 @@ export class OpenAi {
|
|
|
216
364
|
};
|
|
217
365
|
returnObjectCompletionParams.push(instructionMessageParam, ...messageParams);
|
|
218
366
|
logger.info(
|
|
219
|
-
`Assistant called function: (${toolCallId}) ${f.definition.name} => ${JSON.stringify(messageParams)}`,
|
|
367
|
+
`Assistant called function: (${toolCallId}) ${f.definition.name} => ${JSON.stringify(messageParams, null, 2)}`,
|
|
220
368
|
500
|
|
221
369
|
);
|
|
222
370
|
} else {
|
|
@@ -228,7 +376,7 @@ export class OpenAi {
|
|
|
228
376
|
content: serializedReturnObject,
|
|
229
377
|
});
|
|
230
378
|
logger.info(
|
|
231
|
-
`Assistant called function: (${toolCallId}) ${f.definition.name} => ${
|
|
379
|
+
`Assistant called function: (${toolCallId}) ${f.definition.name} => ${JSON.stringify(returnObject, null, 2)}`,
|
|
232
380
|
1000
|
|
233
381
|
);
|
|
234
382
|
}
|
|
@@ -245,9 +393,9 @@ export class OpenAi {
|
|
|
245
393
|
|
|
246
394
|
return returnObjectCompletionParams;
|
|
247
395
|
} catch (error: any) {
|
|
248
|
-
const errorMessage = `Error occurred while executing function ${f.definition.name}: ${error.message}`;
|
|
396
|
+
const errorMessage = `Error occurred while executing function ${f.definition.name}: (${toolCallId}) ${error.message}`;
|
|
249
397
|
logger.error(errorMessage);
|
|
250
|
-
|
|
398
|
+
throw error;
|
|
251
399
|
}
|
|
252
400
|
}
|
|
253
401
|
|