openlayer 0.1.28 → 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.
@@ -200,11 +201,14 @@ export declare class OpenAIMonitor {
200
201
  */
201
202
  constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineName, openlayerServerUrl, }: OpenAIMonitorConstructorProps);
202
203
  private cost;
203
- private formatChatCompletionInput;
204
+ private chatCompletionPrompt;
205
+ private threadPrompt;
206
+ private inputVariables;
204
207
  /**
205
208
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
206
209
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
207
210
  * @param {RequestOptions} [options] - Optional request options.
211
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
208
212
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
209
213
  * @throws {Error} Throws errors from the OpenAI client.
210
214
  */
@@ -213,10 +217,20 @@ export declare class OpenAIMonitor {
213
217
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
214
218
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
215
219
  * @param {RequestOptions} [options] - Optional request options.
220
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
216
221
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
217
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
@@ -310,20 +310,64 @@ class OpenAIMonitor {
310
310
  ? undefined
311
311
  : (inputCost !== null && inputCost !== void 0 ? inputCost : 0) + (outputCost !== null && outputCost !== void 0 ? outputCost : 0);
312
312
  };
313
- this.formatChatCompletionInput = (messages) => messages.map(({ content, role }, i) => ({
313
+ this.chatCompletionPrompt = (fromMessages) => fromMessages.map(({ content, role }, i) => ({
314
314
  content: role === 'user' ? `{{ message_${i} }}` : content,
315
315
  role,
316
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
+ };
317
360
  /**
318
361
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
319
362
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
320
363
  * @param {RequestOptions} [options] - Optional request options.
364
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
321
365
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
322
366
  * @throws {Error} Throws errors from the OpenAI client.
323
367
  */
324
368
  this.createChatCompletion = (body, options, additionalLogs) => __awaiter(this, void 0, void 0, function* () {
325
- var _a, e_1, _b, _c;
326
- var _d, _e, _f, _g, _h, _j, _k;
369
+ var _g, e_2, _h, _j;
370
+ var _k, _l, _m, _o, _p, _q, _r;
327
371
  if (!this.monitoringOn) {
328
372
  console.warn('Monitoring is not active.');
329
373
  }
@@ -337,34 +381,28 @@ class OpenAIMonitor {
337
381
  const response = yield this.openAIClient.chat.completions.create(body, options);
338
382
  try {
339
383
  if (this.monitoringOn && typeof this.inferencePipeline !== 'undefined') {
340
- const prompt = this.formatChatCompletionInput(body.messages);
341
- const inputVariableNames = prompt
342
- .filter(({ role }) => role === 'user')
343
- .map(({ content }) => String(content).replace(/{{\s*|\s*}}/g, ''));
344
- const inputVariables = body.messages
345
- .filter(({ role }) => role === 'user')
346
- .map(({ content }) => content);
347
- 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);
348
386
  const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames,
349
387
  prompt });
350
388
  if (body.stream) {
351
389
  const streamedResponse = response;
352
390
  try {
353
- 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) {
354
- _c = streamedResponse_1_1.value;
355
- _l = false;
356
- 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;
357
395
  // Process each chunk - for example, accumulate input data
358
- 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 : '';
359
397
  streamedOutput += chunkOutput;
360
398
  }
361
399
  }
362
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
400
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
363
401
  finally {
364
402
  try {
365
- 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);
366
404
  }
367
- finally { if (e_1) throw e_1.error; }
405
+ finally { if (e_2) throw e_2.error; }
368
406
  }
369
407
  const endTime = Date.now();
370
408
  const latency = endTime - startTime;
@@ -376,9 +414,9 @@ class OpenAIMonitor {
376
414
  const endTime = Date.now();
377
415
  const latency = endTime - startTime;
378
416
  const output = nonStreamedResponse.choices[0].message.content;
379
- const tokens = (_f = (_e = nonStreamedResponse.usage) === null || _e === void 0 ? void 0 : _e.total_tokens) !== null && _f !== void 0 ? _f : 0;
380
- const inputTokens = (_h = (_g = nonStreamedResponse.usage) === null || _g === void 0 ? void 0 : _g.prompt_tokens) !== null && _h !== void 0 ? _h : 0;
381
- 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;
382
420
  const cost = this.cost(nonStreamedResponse.model, inputTokens, outputTokens);
383
421
  if (typeof output === 'string') {
384
422
  this.openlayerClient.streamData(Object.assign(Object.assign({ cost,
@@ -399,12 +437,13 @@ class OpenAIMonitor {
399
437
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
400
438
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
401
439
  * @param {RequestOptions} [options] - Optional request options.
440
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
402
441
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
403
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.28",
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",