openlayer 0.1.28 → 0.1.30

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.
@@ -71,14 +72,13 @@ interface StreamingDataConfig {
71
72
  }
72
73
  type OpenlayerClientConstructorProps = {
73
74
  openlayerApiKey?: string;
74
- openlayerInferencePipelineName?: string;
75
- openlayerProjectName?: string;
76
75
  openlayerServerUrl?: string;
77
76
  };
78
77
  type OpenAIMonitorConstructorProps = OpenlayerClientConstructorProps & {
79
78
  openAiApiKey: string;
79
+ openlayerInferencePipelineId?: string;
80
80
  openlayerInferencePipelineName?: string;
81
- openlayerProjectName: string;
81
+ openlayerProjectName?: string;
82
82
  };
83
83
  type OpenlayerInferencePipeline = {
84
84
  dataVolumeGraphs?: OpenlayerSampleVolumeGraph;
@@ -187,24 +187,25 @@ export declare class OpenlayerClient {
187
187
  streamData: (data: StreamingData, config: StreamingDataConfig, inferencePipelineId: string) => Promise<void>;
188
188
  }
189
189
  export declare class OpenAIMonitor {
190
- private inferencePipeline?;
191
190
  private openlayerClient;
192
191
  private openAIClient;
193
- private openlayerProjectName;
192
+ private openlayerProjectName?;
193
+ private openlayerInferencePipelineId?;
194
194
  private openlayerInferencePipelineName;
195
- private monitoringOn;
196
- private project?;
197
195
  /**
198
196
  * Constructs an OpenAIMonitor instance.
199
197
  * @param {OpenAIMonitorConstructorProps} props - The configuration properties for the OpenAI and Openlayer clients.
200
198
  */
201
- constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineName, openlayerServerUrl, }: OpenAIMonitorConstructorProps);
199
+ constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineId, openlayerInferencePipelineName, openlayerServerUrl, }: OpenAIMonitorConstructorProps);
202
200
  private cost;
203
- private formatChatCompletionInput;
201
+ private chatCompletionPrompt;
202
+ private threadPrompt;
203
+ private inputVariables;
204
204
  /**
205
205
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
206
206
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
207
207
  * @param {RequestOptions} [options] - Optional request options.
208
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
208
209
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
209
210
  * @throws {Error} Throws errors from the OpenAI client.
210
211
  */
@@ -213,17 +214,23 @@ export declare class OpenAIMonitor {
213
214
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
214
215
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
215
216
  * @param {RequestOptions} [options] - Optional request options.
217
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
216
218
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
217
219
  * @throws {Error} Throws errors from the OpenAI client.
218
220
  */
219
221
  createCompletion: (body: CompletionCreateParams, options?: RequestOptions, additionalLogs?: StreamingData) => Promise<Completion | Stream<Completion>>;
220
222
  /**
221
- * Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
223
+ * Monitor a run from an OpenAI assistant.
224
+ * Once the run is completed, the thread data is published to Openlayer,
225
+ * along with the latency, cost, and number of tokens used.
226
+ * @param {Run} run - The run created by the OpenAI assistant.
227
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
228
+ * @returns {Promise<void>} A promise that resolves when the run data has been successfully published to Openlayer.
222
229
  */
223
- startMonitoring(): Promise<void>;
230
+ monitorThreadRun(run: Run, additionalLogs?: StreamingData): Promise<void>;
224
231
  /**
225
- * Stops monitoring for the OpenAI Monitor instance. If monitoring is not active, a warning is logged.
232
+ * Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
226
233
  */
227
- stopMonitoring(): void;
234
+ initialize(): Promise<void>;
228
235
  }
229
236
  export {};
package/dist/index.js CHANGED
@@ -295,9 +295,8 @@ class OpenAIMonitor {
295
295
  * Constructs an OpenAIMonitor instance.
296
296
  * @param {OpenAIMonitorConstructorProps} props - The configuration properties for the OpenAI and Openlayer clients.
297
297
  */
298
- constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineName, openlayerServerUrl, }) {
298
+ constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineId, openlayerInferencePipelineName, openlayerServerUrl, }) {
299
299
  this.openlayerInferencePipelineName = 'production';
300
- this.monitoringOn = false;
301
300
  this.cost = (model, inputTokens, outputTokens) => {
302
301
  const pricing = OpenAIPricing[model];
303
302
  const inputCost = typeof pricing === 'undefined'
@@ -310,24 +309,65 @@ class OpenAIMonitor {
310
309
  ? undefined
311
310
  : (inputCost !== null && inputCost !== void 0 ? inputCost : 0) + (outputCost !== null && outputCost !== void 0 ? outputCost : 0);
312
311
  };
313
- this.formatChatCompletionInput = (messages) => messages.map(({ content, role }, i) => ({
312
+ this.chatCompletionPrompt = (fromMessages) => fromMessages.map(({ content, role }, i) => ({
314
313
  content: role === 'user' ? `{{ message_${i} }}` : content,
315
314
  role,
316
315
  }));
316
+ this.threadPrompt = (fromMessages) => __awaiter(this, void 0, void 0, function* () {
317
+ var _a, e_1, _b, _c;
318
+ const messages = [];
319
+ try {
320
+ for (var _d = true, _e = __asyncValues(fromMessages.iterPages()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
321
+ _c = _f.value;
322
+ _d = false;
323
+ const page = _c;
324
+ messages.push(...page.getPaginatedItems());
325
+ }
326
+ }
327
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
328
+ finally {
329
+ try {
330
+ if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
331
+ }
332
+ finally { if (e_1) throw e_1.error; }
333
+ }
334
+ return messages
335
+ .map(({ content, role }) => content.map((item) => ({
336
+ content: (() => {
337
+ switch (item.type) {
338
+ case 'image_file':
339
+ return item.image_file.file_id;
340
+ case 'text':
341
+ default:
342
+ return item.text.value;
343
+ }
344
+ })(),
345
+ role,
346
+ })))
347
+ .flat();
348
+ });
349
+ this.inputVariables = (fromPrompt, andMessages) => {
350
+ const inputVariableNames = fromPrompt
351
+ .filter(({ role }) => role === 'user')
352
+ .map(({ content }) => String(content).replace(/{{\s*|\s*}}/g, ''));
353
+ const inputVariables = andMessages
354
+ .filter(({ role }) => role === 'user')
355
+ .map(({ content }) => content);
356
+ const inputVariablesMap = inputVariableNames.reduce((acc, name, i) => (Object.assign(Object.assign({}, acc), { [name]: inputVariables[i] })), {});
357
+ return { inputVariableNames, inputVariables, inputVariablesMap };
358
+ };
317
359
  /**
318
360
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
319
361
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
320
362
  * @param {RequestOptions} [options] - Optional request options.
363
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
321
364
  * @returns {Promise<ChatCompletion | Stream<ChatCompletionChunk>>} Promise of a ChatCompletion or a Stream
322
365
  * @throws {Error} Throws errors from the OpenAI client.
323
366
  */
324
367
  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;
327
- if (!this.monitoringOn) {
328
- console.warn('Monitoring is not active.');
329
- }
330
- else if (typeof this.inferencePipeline === 'undefined') {
368
+ var _g, e_2, _h, _j;
369
+ var _k, _l, _m, _o, _p, _q, _r;
370
+ if (typeof this.openlayerInferencePipelineId === 'undefined') {
331
371
  console.error('No inference pipeline found.');
332
372
  }
333
373
  // Start a timer to measure latency
@@ -336,39 +376,33 @@ class OpenAIMonitor {
336
376
  let streamedOutput = '';
337
377
  const response = yield this.openAIClient.chat.completions.create(body, options);
338
378
  try {
339
- 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] })), {});
379
+ if (typeof this.openlayerInferencePipelineId !== 'undefined') {
380
+ const prompt = this.chatCompletionPrompt(body.messages);
381
+ const { inputVariableNames, inputVariablesMap } = this.inputVariables(prompt, body.messages);
348
382
  const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames,
349
383
  prompt });
350
384
  if (body.stream) {
351
385
  const streamedResponse = response;
352
386
  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;
387
+ 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) {
388
+ _j = streamedResponse_1_1.value;
389
+ _s = false;
390
+ const chunk = _j;
357
391
  // Process each chunk - for example, accumulate input data
358
- const chunkOutput = (_d = chunk.choices[0].delta.content) !== null && _d !== void 0 ? _d : '';
392
+ const chunkOutput = (_k = chunk.choices[0].delta.content) !== null && _k !== void 0 ? _k : '';
359
393
  streamedOutput += chunkOutput;
360
394
  }
361
395
  }
362
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
396
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
363
397
  finally {
364
398
  try {
365
- if (!_l && !_a && (_b = streamedResponse_1.return)) yield _b.call(streamedResponse_1);
399
+ if (!_s && !_g && (_h = streamedResponse_1.return)) yield _h.call(streamedResponse_1);
366
400
  }
367
- finally { if (e_1) throw e_1.error; }
401
+ finally { if (e_2) throw e_2.error; }
368
402
  }
369
403
  const endTime = Date.now();
370
404
  const latency = endTime - startTime;
371
- this.openlayerClient.streamData(Object.assign(Object.assign({ latency, output: streamedOutput, timestamp: startTime }, inputVariablesMap), additionalLogs), config, this.inferencePipeline.id);
405
+ this.openlayerClient.streamData(Object.assign(Object.assign({ latency, output: streamedOutput, timestamp: startTime }, inputVariablesMap), additionalLogs), config, this.openlayerInferencePipelineId);
372
406
  }
373
407
  else {
374
408
  const nonStreamedResponse = response;
@@ -376,13 +410,13 @@ class OpenAIMonitor {
376
410
  const endTime = Date.now();
377
411
  const latency = endTime - startTime;
378
412
  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;
413
+ const tokens = (_m = (_l = nonStreamedResponse.usage) === null || _l === void 0 ? void 0 : _l.total_tokens) !== null && _m !== void 0 ? _m : 0;
414
+ const inputTokens = (_p = (_o = nonStreamedResponse.usage) === null || _o === void 0 ? void 0 : _o.prompt_tokens) !== null && _p !== void 0 ? _p : 0;
415
+ const outputTokens = (_r = (_q = nonStreamedResponse.usage) === null || _q === void 0 ? void 0 : _q.completion_tokens) !== null && _r !== void 0 ? _r : 0;
382
416
  const cost = this.cost(nonStreamedResponse.model, inputTokens, outputTokens);
383
417
  if (typeof output === 'string') {
384
418
  this.openlayerClient.streamData(Object.assign(Object.assign({ cost,
385
- latency, model: nonStreamedResponse.model, output, timestamp: startTime, tokens }, inputVariablesMap), additionalLogs), config, this.inferencePipeline.id);
419
+ latency, model: nonStreamedResponse.model, output, timestamp: startTime, tokens }, inputVariablesMap), additionalLogs), config, this.openlayerInferencePipelineId);
386
420
  }
387
421
  else {
388
422
  console.error('No output received from OpenAI.');
@@ -399,19 +433,17 @@ class OpenAIMonitor {
399
433
  * Creates a completion using the OpenAI client and streams the result to Openlayer.
400
434
  * @param {CompletionCreateParams} body - The parameters for creating a completion.
401
435
  * @param {RequestOptions} [options] - Optional request options.
436
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
402
437
  * @returns {Promise<Completion | Stream<Completion>>} Promise that resolves to a Completion or a Stream.
403
438
  * @throws {Error} Throws errors from the OpenAI client.
404
439
  */
405
440
  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;
441
+ var _t, e_3, _u, _v;
442
+ var _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
408
443
  if (!body.prompt) {
409
444
  console.error('No prompt provided.');
410
445
  }
411
- if (!this.monitoringOn) {
412
- console.warn('Monitoring is not active.');
413
- }
414
- else if (typeof this.inferencePipeline === 'undefined') {
446
+ if (typeof this.openlayerInferencePipelineId === 'undefined') {
415
447
  console.error('No inference pipeline found.');
416
448
  }
417
449
  // Start a timer to measure latency
@@ -424,45 +456,45 @@ class OpenAIMonitor {
424
456
  let streamedOutputTokens = 0;
425
457
  const response = yield this.openAIClient.completions.create(body, options);
426
458
  try {
427
- if (this.monitoringOn && typeof this.inferencePipeline !== 'undefined') {
459
+ if (typeof this.openlayerInferencePipelineId !== 'undefined') {
428
460
  const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames: ['input'] });
429
461
  if (body.stream) {
430
462
  const streamedResponse = response;
431
463
  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;
464
+ 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) {
465
+ _v = streamedResponse_2_1.value;
466
+ _8 = false;
467
+ const chunk = _v;
436
468
  // Process each chunk - for example, accumulate input data
437
469
  streamedModel = chunk.model;
438
470
  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;
471
+ streamedTokens += (_x = (_w = chunk.usage) === null || _w === void 0 ? void 0 : _w.total_tokens) !== null && _x !== void 0 ? _x : 0;
472
+ streamedInputTokens += (_z = (_y = chunk.usage) === null || _y === void 0 ? void 0 : _y.prompt_tokens) !== null && _z !== void 0 ? _z : 0;
473
+ streamedOutputTokens += (_1 = (_0 = chunk.usage) === null || _0 === void 0 ? void 0 : _0.completion_tokens) !== null && _1 !== void 0 ? _1 : 0;
442
474
  }
443
475
  }
444
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
476
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
445
477
  finally {
446
478
  try {
447
- if (!_2 && !_m && (_o = streamedResponse_2.return)) yield _o.call(streamedResponse_2);
479
+ if (!_8 && !_t && (_u = streamedResponse_2.return)) yield _u.call(streamedResponse_2);
448
480
  }
449
- finally { if (e_2) throw e_2.error; }
481
+ finally { if (e_3) throw e_3.error; }
450
482
  }
451
483
  const endTime = Date.now();
452
484
  const latency = endTime - startTime;
453
485
  const cost = this.cost(streamedModel, streamedInputTokens, streamedOutputTokens);
454
- this.openlayerClient.streamData(Object.assign({ cost, input: body.prompt, latency, output: streamedOutput, timestamp: startTime, tokens: streamedTokens }, additionalLogs), config, this.inferencePipeline.id);
486
+ this.openlayerClient.streamData(Object.assign({ cost, input: body.prompt, latency, output: streamedOutput, timestamp: startTime, tokens: streamedTokens }, additionalLogs), config, this.openlayerInferencePipelineId);
455
487
  }
456
488
  else {
457
489
  const nonStreamedResponse = response;
458
490
  // Handle regular (non-streamed) response
459
491
  const endTime = Date.now();
460
492
  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;
493
+ const tokens = (_3 = (_2 = nonStreamedResponse.usage) === null || _2 === void 0 ? void 0 : _2.total_tokens) !== null && _3 !== void 0 ? _3 : 0;
494
+ const inputTokens = (_5 = (_4 = nonStreamedResponse.usage) === null || _4 === void 0 ? void 0 : _4.prompt_tokens) !== null && _5 !== void 0 ? _5 : 0;
495
+ const outputTokens = (_7 = (_6 = nonStreamedResponse.usage) === null || _6 === void 0 ? void 0 : _6.completion_tokens) !== null && _7 !== void 0 ? _7 : 0;
464
496
  const cost = this.cost(nonStreamedResponse.model, inputTokens, outputTokens);
465
- this.openlayerClient.streamData(Object.assign({ cost, input: body.prompt, latency, output: nonStreamedResponse.choices[0].text, timestamp: startTime, tokens }, additionalLogs), config, this.inferencePipeline.id);
497
+ this.openlayerClient.streamData(Object.assign({ cost, input: body.prompt, latency, output: nonStreamedResponse.choices[0].text, timestamp: startTime, tokens }, additionalLogs), config, this.openlayerInferencePipelineId);
466
498
  }
467
499
  }
468
500
  }
@@ -472,6 +504,7 @@ class OpenAIMonitor {
472
504
  return response;
473
505
  });
474
506
  this.openlayerProjectName = openlayerProjectName;
507
+ this.openlayerInferencePipelineId = openlayerInferencePipelineId;
475
508
  if (openlayerInferencePipelineName) {
476
509
  this.openlayerInferencePipelineName = openlayerInferencePipelineName;
477
510
  }
@@ -484,43 +517,91 @@ class OpenAIMonitor {
484
517
  dangerouslyAllowBrowser: true,
485
518
  });
486
519
  }
520
+ /**
521
+ * Monitor a run from an OpenAI assistant.
522
+ * Once the run is completed, the thread data is published to Openlayer,
523
+ * along with the latency, cost, and number of tokens used.
524
+ * @param {Run} run - The run created by the OpenAI assistant.
525
+ * @param {StreamingData} [additionalLogs] - Optional metadata logs to include with the request sent to Openlayer.
526
+ * @returns {Promise<void>} A promise that resolves when the run data has been successfully published to Openlayer.
527
+ */
528
+ monitorThreadRun(run, additionalLogs) {
529
+ var _a;
530
+ return __awaiter(this, void 0, void 0, function* () {
531
+ if (typeof this.openlayerInferencePipelineId === 'undefined') {
532
+ console.error('No inference pipeline found.');
533
+ return;
534
+ }
535
+ if (run.status !== 'completed') {
536
+ return;
537
+ }
538
+ try {
539
+ const { assistant_id, completed_at, created_at, model, thread_id,
540
+ // @ts-ignore
541
+ usage, } = run;
542
+ // @ts-ignore
543
+ const { completion_tokens, prompt_tokens, total_tokens } = typeof usage === 'undefined' ||
544
+ typeof usage !== 'object' ||
545
+ usage === null
546
+ ? {}
547
+ : usage;
548
+ const cost = this.cost(model, prompt_tokens, completion_tokens);
549
+ const latency = completed_at === null ||
550
+ created_at === null ||
551
+ isNaN(completed_at) ||
552
+ isNaN(created_at)
553
+ ? undefined
554
+ : (completed_at - created_at) * 1000;
555
+ const messages = yield this.openAIClient.beta.threads.messages.list(thread_id, { order: 'asc' });
556
+ const populatedPrompt = yield this.threadPrompt(messages);
557
+ const prompt = this.chatCompletionPrompt(populatedPrompt);
558
+ const { inputVariableNames, inputVariablesMap } = this.inputVariables(prompt, populatedPrompt);
559
+ const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames, prompt: prompt.slice(0, prompt.length - 1) });
560
+ const output = (_a = prompt[prompt.length - 1]) === null || _a === void 0 ? void 0 : _a.content;
561
+ const resolvedOutput = typeof output === 'string'
562
+ ? output
563
+ : typeof output === 'undefined' || output === null
564
+ ? ''
565
+ : `${output}`;
566
+ this.openlayerClient.streamData(Object.assign(Object.assign({ 'OpenAI Assistant ID': assistant_id, 'OpenAI Thread ID': thread_id, cost,
567
+ latency, output: resolvedOutput, timestamp: run.created_at, tokens: total_tokens }, inputVariablesMap), additionalLogs), config, this.openlayerInferencePipelineId);
568
+ }
569
+ catch (error) {
570
+ console.error('Error logging thread run:', error);
571
+ }
572
+ });
573
+ }
487
574
  /**
488
575
  * Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
489
576
  */
490
- startMonitoring() {
577
+ initialize() {
491
578
  return __awaiter(this, void 0, void 0, function* () {
492
- if (this.monitoringOn) {
493
- console.warn('Monitor is already on.');
579
+ console.info('Initializing monitor: creating or loading an Openlayer project and inference pipeline...');
580
+ if (typeof this.openlayerInferencePipelineId !== 'undefined') {
581
+ console.info('Monitor initialized: using inference pipeline ID provided.');
494
582
  return;
495
583
  }
496
- console.info('Starting monitor: creating or loading an Openlayer project and inference pipeline...');
497
584
  try {
498
- this.monitoringOn = true;
499
- this.project = yield this.openlayerClient.createProject(this.openlayerProjectName, 'llm-base');
500
- if (typeof this.project !== 'undefined') {
501
- this.inferencePipeline =
502
- yield this.openlayerClient.createInferencePipeline(this.project.id, this.openlayerInferencePipelineName);
585
+ if (typeof this.openlayerProjectName === 'undefined') {
586
+ console.error('No project name provided.');
587
+ return;
588
+ }
589
+ const project = yield this.openlayerClient.createProject(this.openlayerProjectName, 'llm-base');
590
+ if (typeof project !== 'undefined') {
591
+ const inferencePipeline = yield this.openlayerClient.createInferencePipeline(project.id, this.openlayerInferencePipelineName);
592
+ if (typeof (inferencePipeline === null || inferencePipeline === void 0 ? void 0 : inferencePipeline.id) === 'undefined') {
593
+ console.error('Unable to locate inference pipeline.');
594
+ }
595
+ else {
596
+ this.openlayerInferencePipelineId = inferencePipeline.id;
597
+ }
503
598
  }
504
599
  console.info('Monitor started');
505
600
  }
506
601
  catch (error) {
507
602
  console.error('An error occurred while starting the monitor:', error);
508
- this.stopMonitoring();
509
603
  }
510
604
  });
511
605
  }
512
- /**
513
- * Stops monitoring for the OpenAI Monitor instance. If monitoring is not active, a warning is logged.
514
- */
515
- stopMonitoring() {
516
- if (!this.monitoringOn) {
517
- console.warn('Monitor is not active.');
518
- return;
519
- }
520
- this.monitoringOn = false;
521
- this.project = undefined;
522
- this.inferencePipeline = undefined;
523
- console.info('Monitor stopped.');
524
- }
525
606
  }
526
607
  exports.OpenAIMonitor = OpenAIMonitor;
@@ -0,0 +1,56 @@
1
+ /*
2
+ * This example shows how to use Openlayer to monitor runs from OpenAI assistants.
3
+ */
4
+
5
+ import OpenAI from 'openai';
6
+ import { OpenAIMonitor } from 'openlayer';
7
+
8
+ const openai = new OpenAI({
9
+ apiKey: 'YOUR_OPENAI_API_KEY',
10
+ });
11
+
12
+ // Create monitor with your credentials
13
+ const monitor = new OpenAIMonitor({
14
+ openAiApiKey: 'YOUR_OPENAI_API_KEY',
15
+ openlayerApiKey: 'YOUR_OPENLAYER_API_KEY',
16
+ // EITHER specify an existing inference pipeline ID
17
+ openlayerInferencePipelineId: 'YOUR_OPENLAYER_INFERENCE_PIPELINE_ID',
18
+ // OR the project and inference pipeline names to create or load one
19
+ openlayerInferencePipelineName: 'production',
20
+ openlayerProjectName: 'YOUR_OPENLAYER_PROJECT_NAME',
21
+ });
22
+
23
+ await monitor.initialize();
24
+
25
+ // Create the assistant
26
+ const assistant = await openai.beta.assistants.create({
27
+ description:
28
+ 'You are great at creating and explaining beautiful data visualizations.',
29
+ model: 'gpt-4',
30
+ name: 'Data visualizer',
31
+ tools: [{ type: 'code_interpreter' }],
32
+ });
33
+
34
+ // Create a thread
35
+ const thread = await openai.beta.threads.create({
36
+ messages: [
37
+ {
38
+ content: 'Create a data visualization of the american GDP.',
39
+ role: 'user',
40
+ },
41
+ ],
42
+ });
43
+
44
+ // Run the assistant on the thread
45
+ const run = await openai.beta.threads.runs.create(thread.id, {
46
+ assistant_id: assistant.id,
47
+ });
48
+
49
+ // Keep polling the run results
50
+ let runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);
51
+ while (runStatus.status !== 'completed') {
52
+ runStatus = await openai.beta.threads.runs.retrieve(thread.id, run.id);
53
+
54
+ // Monitor the run. If complete, it will be sent to Openlayer
55
+ await monitor.monitorThreadRun(runStatus);
56
+ }
@@ -7,11 +7,14 @@ import { OpenAIMonitor } from 'openlayer';
7
7
  const monitor = new OpenAIMonitor({
8
8
  openAiApiKey: 'YOUR_OPENAI_API_KEY',
9
9
  openlayerApiKey: 'YOUR_OPENLAYER_API_KEY',
10
+ // EITHER specify an existing inference pipeline ID
11
+ openlayerInferencePipelineId: 'YOUR_OPENLAYER_INFERENCE_PIPELINE_ID',
12
+ // OR the project and inference pipeline names to create or load one
10
13
  openlayerInferencePipelineName: 'production',
11
14
  openlayerProjectName: 'YOUR_OPENLAYER_PROJECT_NAME',
12
15
  });
13
16
 
14
- await monitor.startMonitoring();
17
+ await monitor.initialize();
15
18
 
16
19
  const inputs = [
17
20
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openlayer",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "The Openlayer TypeScript client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",