openlayer 0.1.27 → 0.1.29

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/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RequestOptions } from 'openai/core';
2
2
  import { ChatCompletion, ChatCompletionChunk, ChatCompletionCreateParams, ChatCompletionMessageParam, Completion, CompletionCreateParams } from 'openai/resources';
3
+ import { Run } from 'openai/resources/beta/threads/runs/runs';
3
4
  import { Stream } from 'openai/streaming';
4
5
  /**
5
6
  * Represents the data structure for a chat completion.
@@ -143,7 +144,6 @@ export declare class OpenlayerClient {
143
144
  /**
144
145
  * Constructs an OpenlayerClient instance.
145
146
  * @param {OpenlayerClientConstructorProps} props - The config for the Openlayer client. The API key is required.
146
- * @throws {Error} Throws an error if the Openlayer API key is not provided.
147
147
  */
148
148
  constructor({ openlayerApiKey, openlayerServerUrl, }: OpenlayerClientConstructorProps);
149
149
  private resolvedQuery;
@@ -201,22 +201,36 @@ export declare class OpenAIMonitor {
201
201
  */
202
202
  constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineName, openlayerServerUrl, }: OpenAIMonitorConstructorProps);
203
203
  private cost;
204
- private formatChatCompletionInput;
204
+ private chatCompletionPrompt;
205
+ private threadPrompt;
206
+ private inputVariables;
205
207
  /**
206
208
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
207
209
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
208
210
  * @param {RequestOptions} [options] - Optional request options.
211
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
209
212
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
210
- * @throws {Error} Throws an error if monitoring is not active or if no output is received from OpenAI.
213
+ * @throws {Error} Throws errors from the OpenAI client.
211
214
  */
212
215
  createChatCompletion: (body: ChatCompletionCreateParams, options?: RequestOptions, additionalLogs?: StreamingData) => Promise<ChatCompletion | Stream<ChatCompletionChunk>>;
213
216
  /**
214
217
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
215
218
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
216
219
  * @param {RequestOptions} [options] - Optional request options.
220
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
217
221
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
222
+ * @throws {Error} Throws errors from the OpenAI client.
218
223
  */
219
224
  createCompletion: (body: CompletionCreateParams, options?: RequestOptions, additionalLogs?: StreamingData) => Promise<Completion | Stream<Completion>>;
225
+ /**
226
+ * Monitor a run from an OpenAI assistant.
227
+ * Once the run is completed, the thread data is published to Openlayer,
228
+ * along with the latency, cost, and number of tokens used.
229
+ * @param {Run} run - The run created by the OpenAI assistant.
230
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
231
+ * @returns {Promise<void>} A promise that resolves when the run data has been successfully published to Openlayer.
232
+ */
233
+ logThreadRun(run: Run, additionalLogs?: StreamingData): Promise<void>;
220
234
  /**
221
235
  * Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
222
236
  */
package/dist/index.js CHANGED
@@ -98,7 +98,6 @@ class OpenlayerClient {
98
98
  /**
99
99
  * Constructs an OpenlayerClient instance.
100
100
  * @param {OpenlayerClientConstructorProps} props - The config for the Openlayer client. The API key is required.
101
- * @throws {Error} Throws an error if the Openlayer API key is not provided.
102
101
  */
103
102
  constructor({ openlayerApiKey, openlayerServerUrl, }) {
104
103
  this.defaultConfig = {
@@ -311,20 +310,64 @@ class OpenAIMonitor {
311
310
  ? undefined
312
311
  : (inputCost !== null && inputCost !== void 0 ? inputCost : 0) + (outputCost !== null && outputCost !== void 0 ? outputCost : 0);
313
312
  };
314
- this.formatChatCompletionInput = (messages) => messages.map(({ content, role }, i) => ({
313
+ this.chatCompletionPrompt = (fromMessages) => fromMessages.map(({ content, role }, i) => ({
315
314
  content: role === 'user' ? `{{ message_${i} }}` : content,
316
315
  role,
317
316
  }));
317
+ this.threadPrompt = (fromMessages) => __awaiter(this, void 0, void 0, function* () {
318
+ var _a, e_1, _b, _c;
319
+ const messages = [];
320
+ try {
321
+ for (var _d = true, _e = __asyncValues(fromMessages.iterPages()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
322
+ _c = _f.value;
323
+ _d = false;
324
+ const page = _c;
325
+ messages.push(...page.getPaginatedItems());
326
+ }
327
+ }
328
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
329
+ finally {
330
+ try {
331
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
332
+ }
333
+ finally { if (e_1) throw e_1.error; }
334
+ }
335
+ return messages
336
+ .map(({ content, role }) => content.map((item) => ({
337
+ content: (() => {
338
+ switch (item.type) {
339
+ case 'image_file':
340
+ return item.image_file.file_id;
341
+ case 'text':
342
+ default:
343
+ return item.text.value;
344
+ }
345
+ })(),
346
+ role,
347
+ })))
348
+ .flat();
349
+ });
350
+ this.inputVariables = (fromPrompt, andMessages) => {
351
+ const inputVariableNames = fromPrompt
352
+ .filter(({ role }) => role === 'user')
353
+ .map(({ content }) => String(content).replace(/{{\s*|\s*}}/g, ''));
354
+ const inputVariables = andMessages
355
+ .filter(({ role }) => role === 'user')
356
+ .map(({ content }) => content);
357
+ const inputVariablesMap = inputVariableNames.reduce((acc, name, i) => (Object.assign(Object.assign({}, acc), { [name]: inputVariables[i] })), {});
358
+ return { inputVariableNames, inputVariables, inputVariablesMap };
359
+ };
318
360
  /**
319
361
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
320
362
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
321
363
  * @param {RequestOptions} [options] - Optional request options.
364
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
322
365
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
323
- * @throws {Error} Throws an error if monitoring is not active or if no output is received from OpenAI.
366
+ * @throws {Error} Throws errors from the OpenAI client.
324
367
  */
325
368
  this.createChatCompletion = (body, options, additionalLogs) => __awaiter(this, void 0, void 0, function* () {
326
- var _a, e_1, _b, _c;
327
- var _d, _e, _f, _g, _h, _j, _k;
369
+ var _g, e_2, _h, _j;
370
+ var _k, _l, _m, _o, _p, _q, _r;
328
371
  if (!this.monitoringOn) {
329
372
  console.warn('Monitoring is not active.');
330
373
  }
@@ -338,34 +381,28 @@ class OpenAIMonitor {
338
381
  const response = yield this.openAIClient.chat.completions.create(body, options);
339
382
  try {
340
383
  if (this.monitoringOn && typeof this.inferencePipeline !== 'undefined') {
341
- const prompt = this.formatChatCompletionInput(body.messages);
342
- const inputVariableNames = prompt
343
- .filter(({ role }) => role === 'user')
344
- .map(({ content }) => String(content).replace(/{{\s*|\s*}}/g, ''));
345
- const inputVariables = body.messages
346
- .filter(({ role }) => role === 'user')
347
- .map(({ content }) => content);
348
- const inputVariablesMap = inputVariableNames.reduce((acc, name, i) => (Object.assign(Object.assign({}, acc), { [name]: inputVariables[i] })), {});
384
+ const prompt = this.chatCompletionPrompt(body.messages);
385
+ const { inputVariableNames, inputVariablesMap } = this.inputVariables(prompt, body.messages);
349
386
  const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames,
350
387
  prompt });
351
388
  if (body.stream) {
352
389
  const streamedResponse = response;
353
390
  try {
354
- for (var _l = true, streamedResponse_1 = __asyncValues(streamedResponse), streamedResponse_1_1; streamedResponse_1_1 = yield streamedResponse_1.next(), _a = streamedResponse_1_1.done, !_a; _l = true) {
355
- _c = streamedResponse_1_1.value;
356
- _l = false;
357
- const chunk = _c;
391
+ for (var _s = true, streamedResponse_1 = __asyncValues(streamedResponse), streamedResponse_1_1; streamedResponse_1_1 = yield streamedResponse_1.next(), _g = streamedResponse_1_1.done, !_g; _s = true) {
392
+ _j = streamedResponse_1_1.value;
393
+ _s = false;
394
+ const chunk = _j;
358
395
  // Process each chunk - for example, accumulate input data
359
- const chunkOutput = (_d = chunk.choices[0].delta.content) !== null && _d !== void 0 ? _d : '';
396
+ const chunkOutput = (_k = chunk.choices[0].delta.content) !== null && _k !== void 0 ? _k : '';
360
397
  streamedOutput += chunkOutput;
361
398
  }
362
399
  }
363
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
400
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
364
401
  finally {
365
402
  try {
366
- if (!_l && !_a && (_b = streamedResponse_1.return)) yield _b.call(streamedResponse_1);
403
+ if (!_s && !_g && (_h = streamedResponse_1.return)) yield _h.call(streamedResponse_1);
367
404
  }
368
- finally { if (e_1) throw e_1.error; }
405
+ finally { if (e_2) throw e_2.error; }
369
406
  }
370
407
  const endTime = Date.now();
371
408
  const latency = endTime - startTime;
@@ -377,9 +414,9 @@ class OpenAIMonitor {
377
414
  const endTime = Date.now();
378
415
  const latency = endTime - startTime;
379
416
  const output = nonStreamedResponse.choices[0].message.content;
380
- const tokens = (_f = (_e = nonStreamedResponse.usage) === null || _e === void 0 ? void 0 : _e.total_tokens) !== null && _f !== void 0 ? _f : 0;
381
- const inputTokens = (_h = (_g = nonStreamedResponse.usage) === null || _g === void 0 ? void 0 : _g.prompt_tokens) !== null && _h !== void 0 ? _h : 0;
382
- const outputTokens = (_k = (_j = nonStreamedResponse.usage) === null || _j === void 0 ? void 0 : _j.completion_tokens) !== null && _k !== void 0 ? _k : 0;
417
+ const tokens = (_m = (_l = nonStreamedResponse.usage) === null || _l === void 0 ? void 0 : _l.total_tokens) !== null && _m !== void 0 ? _m : 0;
418
+ const inputTokens = (_p = (_o = nonStreamedResponse.usage) === null || _o === void 0 ? void 0 : _o.prompt_tokens) !== null && _p !== void 0 ? _p : 0;
419
+ const outputTokens = (_r = (_q = nonStreamedResponse.usage) === null || _q === void 0 ? void 0 : _q.completion_tokens) !== null && _r !== void 0 ? _r : 0;
383
420
  const cost = this.cost(nonStreamedResponse.model, inputTokens, outputTokens);
384
421
  if (typeof output === 'string') {
385
422
  this.openlayerClient.streamData(Object.assign(Object.assign({ cost,
@@ -400,11 +437,13 @@ class OpenAIMonitor {
400
437
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
401
438
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
402
439
  * @param {RequestOptions} [options] - Optional request options.
440
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
403
441
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
442
+ * @throws {Error} Throws errors from the OpenAI client.
404
443
  */
405
444
  this.createCompletion = (body, options, additionalLogs) => __awaiter(this, void 0, void 0, function* () {
406
- var _m, e_2, _o, _p;
407
- var _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
445
+ var _t, e_3, _u, _v;
446
+ var _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
408
447
  if (!body.prompt) {
409
448
  console.error('No prompt provided.');
410
449
  }
@@ -429,24 +468,24 @@ class OpenAIMonitor {
429
468
  if (body.stream) {
430
469
  const streamedResponse = response;
431
470
  try {
432
- for (var _2 = true, streamedResponse_2 = __asyncValues(streamedResponse), streamedResponse_2_1; streamedResponse_2_1 = yield streamedResponse_2.next(), _m = streamedResponse_2_1.done, !_m; _2 = true) {
433
- _p = streamedResponse_2_1.value;
434
- _2 = false;
435
- const chunk = _p;
471
+ for (var _8 = true, streamedResponse_2 = __asyncValues(streamedResponse), streamedResponse_2_1; streamedResponse_2_1 = yield streamedResponse_2.next(), _t = streamedResponse_2_1.done, !_t; _8 = true) {
472
+ _v = streamedResponse_2_1.value;
473
+ _8 = false;
474
+ const chunk = _v;
436
475
  // Process each chunk - for example, accumulate input data
437
476
  streamedModel = chunk.model;
438
477
  streamedOutput += chunk.choices[0].text.trim();
439
- streamedTokens += (_r = (_q = chunk.usage) === null || _q === void 0 ? void 0 : _q.total_tokens) !== null && _r !== void 0 ? _r : 0;
440
- streamedInputTokens += (_t = (_s = chunk.usage) === null || _s === void 0 ? void 0 : _s.prompt_tokens) !== null && _t !== void 0 ? _t : 0;
441
- streamedOutputTokens += (_v = (_u = chunk.usage) === null || _u === void 0 ? void 0 : _u.completion_tokens) !== null && _v !== void 0 ? _v : 0;
478
+ streamedTokens += (_x = (_w = chunk.usage) === null || _w === void 0 ? void 0 : _w.total_tokens) !== null && _x !== void 0 ? _x : 0;
479
+ streamedInputTokens += (_z = (_y = chunk.usage) === null || _y === void 0 ? void 0 : _y.prompt_tokens) !== null && _z !== void 0 ? _z : 0;
480
+ streamedOutputTokens += (_1 = (_0 = chunk.usage) === null || _0 === void 0 ? void 0 : _0.completion_tokens) !== null && _1 !== void 0 ? _1 : 0;
442
481
  }
443
482
  }
444
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
483
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
445
484
  finally {
446
485
  try {
447
- if (!_2 && !_m && (_o = streamedResponse_2.return)) yield _o.call(streamedResponse_2);
486
+ if (!_8 && !_t && (_u = streamedResponse_2.return)) yield _u.call(streamedResponse_2);
448
487
  }
449
- finally { if (e_2) throw e_2.error; }
488
+ finally { if (e_3) throw e_3.error; }
450
489
  }
451
490
  const endTime = Date.now();
452
491
  const latency = endTime - startTime;
@@ -458,9 +497,9 @@ class OpenAIMonitor {
458
497
  // Handle regular (non-streamed) response
459
498
  const endTime = Date.now();
460
499
  const latency = endTime - startTime;
461
- const tokens = (_x = (_w = nonStreamedResponse.usage) === null || _w === void 0 ? void 0 : _w.total_tokens) !== null && _x !== void 0 ? _x : 0;
462
- const inputTokens = (_z = (_y = nonStreamedResponse.usage) === null || _y === void 0 ? void 0 : _y.prompt_tokens) !== null && _z !== void 0 ? _z : 0;
463
- const outputTokens = (_1 = (_0 = nonStreamedResponse.usage) === null || _0 === void 0 ? void 0 : _0.completion_tokens) !== null && _1 !== void 0 ? _1 : 0;
500
+ const tokens = (_3 = (_2 = nonStreamedResponse.usage) === null || _2 === void 0 ? void 0 : _2.total_tokens) !== null && _3 !== void 0 ? _3 : 0;
501
+ const inputTokens = (_5 = (_4 = nonStreamedResponse.usage) === null || _4 === void 0 ? void 0 : _4.prompt_tokens) !== null && _5 !== void 0 ? _5 : 0;
502
+ const outputTokens = (_7 = (_6 = nonStreamedResponse.usage) === null || _6 === void 0 ? void 0 : _6.completion_tokens) !== null && _7 !== void 0 ? _7 : 0;
464
503
  const cost = this.cost(nonStreamedResponse.model, inputTokens, outputTokens);
465
504
  this.openlayerClient.streamData(Object.assign({ cost, input: body.prompt, latency, output: nonStreamedResponse.choices[0].text, timestamp: startTime, tokens }, additionalLogs), config, this.inferencePipeline.id);
466
505
  }
@@ -484,6 +523,61 @@ class OpenAIMonitor {
484
523
  dangerouslyAllowBrowser: true,
485
524
  });
486
525
  }
526
+ /**
527
+ * Monitor a run from an OpenAI assistant.
528
+ * Once the run is completed, the thread data is published to Openlayer,
529
+ * along with the latency, cost, and number of tokens used.
530
+ * @param {Run} run - The run created by the OpenAI assistant.
531
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
532
+ * @returns {Promise<void>} A promise that resolves when the run data has been successfully published to Openlayer.
533
+ */
534
+ logThreadRun(run, additionalLogs) {
535
+ var _a;
536
+ return __awaiter(this, void 0, void 0, function* () {
537
+ if (typeof this.inferencePipeline === 'undefined') {
538
+ console.error('No inference pipeline found.');
539
+ return;
540
+ }
541
+ if (run.status !== 'completed') {
542
+ return;
543
+ }
544
+ try {
545
+ const { assistant_id, completed_at, created_at, model, thread_id,
546
+ // @ts-ignore
547
+ usage, } = run;
548
+ // @ts-ignore
549
+ const { completion_tokens, prompt_tokens, total_tokens } = typeof usage === 'undefined' ||
550
+ typeof usage !== 'object' ||
551
+ usage === null
552
+ ? {}
553
+ : usage;
554
+ const cost = this.cost(model, prompt_tokens, completion_tokens);
555
+ const latency = completed_at === null ||
556
+ created_at === null ||
557
+ isNaN(completed_at) ||
558
+ isNaN(created_at)
559
+ ? undefined
560
+ : (completed_at - created_at) * 1000;
561
+ const messages = yield this.openAIClient.beta.threads.messages.list(thread_id, { order: 'asc' });
562
+ const populatedPrompt = yield this.threadPrompt(messages);
563
+ const prompt = this.chatCompletionPrompt(populatedPrompt);
564
+ const { inputVariableNames, inputVariablesMap } = this.inputVariables(prompt, populatedPrompt);
565
+ const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames, prompt: prompt.slice(0, prompt.length - 1) });
566
+ const output = (_a = prompt[prompt.length - 1]) === null || _a === void 0 ? void 0 : _a.content;
567
+ const resolvedOutput = typeof output === 'string'
568
+ ? output
569
+ : typeof output === 'undefined' || output === null
570
+ ? ''
571
+ : `${output}`;
572
+ this.openlayerClient.streamData(Object.assign(Object.assign({ assistant_id,
573
+ cost,
574
+ latency, output: resolvedOutput, thread_id, timestamp: run.created_at, tokens: total_tokens }, inputVariablesMap), additionalLogs), config, this.inferencePipeline.id);
575
+ }
576
+ catch (error) {
577
+ console.error('Error logging thread run:', error);
578
+ }
579
+ });
580
+ }
487
581
  /**
488
582
  * Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
489
583
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openlayer",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "The Openlayer TypeScript client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,6 +20,7 @@
20
20
  "eslint-plugin-typescript-sort-keys": "^3.1.0",
21
21
  "node-fetch": "^3.3.2",
22
22
  "openai": "^4.19.0",
23
+ "openlayer": "^0.1.27",
23
24
  "uuid": "^9.0.1"
24
25
  },
25
26
  "devDependencies": {