openlayer 0.1.12 → 0.1.14

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/README.md ADDED
@@ -0,0 +1,35 @@
1
+ <div align="left">
2
+ <img src="static/logo.png"><br>
3
+ </div>
4
+
5
+ # Openlayer | JavaScript/TypeScript Library
6
+
7
+ [![npm version](https://badge.fury.io/js/openlayer.svg)](https://badge.fury.io/js/openlayer)
8
+
9
+ ## What is it?
10
+
11
+ Openlayer is a debugging workspace for ML & Data Science. Openlayer combines and builds upon SOTA techniques in explainability, model and dataset versioning, synthetic data generation, data-centric testing and much more to form a powerful, **unified platform for model development**.
12
+
13
+ 👉 [Join our Discord community!](https://discord.gg/t6wS2g6MMB) We'd love to meet you and help you get started with Openlayer!
14
+
15
+ This is the official JavaScript/TypeScript library for interacting with the Openlayer platform. Navigate [here](https://docs.openlayer.com) for a quickstart guide and for in-depth tutorials.
16
+
17
+ ## Main Features
18
+
19
+ This library's primary function is to enable you to easily package your models and datasets and add them to your Openlayer account.
20
+
21
+ ## Installation
22
+
23
+ Install with npm
24
+
25
+ ```console
26
+ npm i openlayer
27
+ ```
28
+
29
+ ## Documentation
30
+
31
+ The official documentation for this library can be found [here](https://docs.openlayer.com).
32
+
33
+ ## Contributing
34
+
35
+ All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome! Just send us a message on [Discord](https://discord.gg/t6wS2g6MMB).
package/dist/index.d.ts CHANGED
@@ -1,16 +1,12 @@
1
1
  import { RequestOptions } from 'openai/core';
2
- import { ChatCompletion, ChatCompletionChunk, ChatCompletionCreateParams, Completion, CompletionCreateParams } from 'openai/resources';
2
+ import { ChatCompletion, ChatCompletionChunk, ChatCompletionCreateParams, ChatCompletionMessageParam, Completion, CompletionCreateParams } from 'openai/resources';
3
3
  import { Stream } from 'openai/streaming';
4
4
  /**
5
5
  * Represents the data structure for a chat completion.
6
+ * Object keys represent a column name and the values represent the column value.
6
7
  */
7
- export interface ChatCompletionData {
8
- [key: string]: any;
9
- /**
10
- * The input data for the chat completion. It can be a string, an array of strings,
11
- * or a nested array of numbers.
12
- */
13
- input: string | string[] | number[] | number[][];
8
+ export interface StreamingData {
9
+ [columnName: string]: any;
14
10
  /**
15
11
  * The latency of the chat completion in milliseconds. Optional.
16
12
  */
@@ -28,6 +24,43 @@ export interface ChatCompletionData {
28
24
  */
29
25
  tokens?: number;
30
26
  }
27
+ /**
28
+ * Configuration settings for uploading chat completion data to Openlayer.
29
+ */
30
+ interface StreamingDataConfig {
31
+ /**
32
+ * The name of the column that stores the ground truth data. Can be null.
33
+ */
34
+ groundTruthColumnName: string | null;
35
+ /**
36
+ * The name of the column that stores inference IDs. Can be null.
37
+ */
38
+ inferenceIdColumnName: string | null;
39
+ /**
40
+ * An array of names for input variable columns. Can be null.
41
+ */
42
+ inputVariableNames?: string[] | null;
43
+ /**
44
+ * The name of the column that stores latency data. Can be null.
45
+ */
46
+ latencyColumnName: string | null;
47
+ /**
48
+ * The name of the column that stores the number of tokens. Can be null.
49
+ */
50
+ numOfTokenColumnName: string | null;
51
+ /**
52
+ * The name of the column that stores output data. Can be null.
53
+ */
54
+ outputColumnName: string | null;
55
+ /**
56
+ * The full prompt history for the chat completion.
57
+ */
58
+ prompt?: ChatCompletionMessageParam[];
59
+ /**
60
+ * The name of the column that stores timestamp data. Can be null.
61
+ */
62
+ timestampColumnName: string | null;
63
+ }
31
64
  type OpenlayerClientConstructorProps = {
32
65
  openlayerApiKey?: string;
33
66
  openlayerInferencePipelineName?: string;
@@ -96,7 +129,7 @@ type OpenlayerSampleVolumeGraph = {
96
129
  type OpenlayerTaskType = 'llm-base' | 'tabular-classification' | 'tabular-regression' | 'text-classification';
97
130
  export declare class OpenlayerClient {
98
131
  private openlayerApiKey?;
99
- private openlayerDefaultDataConfig;
132
+ defaultConfig: StreamingDataConfig;
100
133
  private openlayerServerUrl;
101
134
  private version;
102
135
  /**
@@ -108,12 +141,12 @@ export declare class OpenlayerClient {
108
141
  private resolvedQuery;
109
142
  /**
110
143
  * Streams data to the Openlayer inference pipeline.
111
- * @param {ChatCompletionData} data - The chat completion data to be streamed.
144
+ * @param {StreamingData} data - The chat completion data to be streamed.
112
145
  * @param {string} inferencePipelineId - The ID of the Openlayer inference pipeline to which data is streamed.
113
146
  * @returns {Promise<void>} A promise that resolves when the data has been successfully streamed.
114
147
  * @throws {Error} Throws an error if the Openlayer API key is not set or an error occurs in the streaming process.
115
148
  */
116
- streamData: (data: ChatCompletionData, inferencePipelineId: string) => Promise<void>;
149
+ streamData: (data: StreamingData, config: StreamingDataConfig, inferencePipelineId: string) => Promise<void>;
117
150
  /**
118
151
  * Creates a new inference pipeline in Openlayer or loads an existing one.
119
152
  * @param {string} projectId - The ID of the project containing the inference pipeline.
package/dist/index.js CHANGED
@@ -27,10 +27,9 @@ class OpenlayerClient {
27
27
  * @throws {Error} Throws an error if the Openlayer API key is not provided.
28
28
  */
29
29
  constructor({ openlayerApiKey, openlayerServerUrl, }) {
30
- this.openlayerDefaultDataConfig = {
30
+ this.defaultConfig = {
31
31
  groundTruthColumnName: null,
32
32
  inferenceIdColumnName: 'id',
33
- inputVariableNames: ['input'],
34
33
  latencyColumnName: 'latency',
35
34
  numOfTokenColumnName: 'tokens',
36
35
  outputColumnName: 'output',
@@ -41,12 +40,12 @@ class OpenlayerClient {
41
40
  this.resolvedQuery = (endpoint, args = {}) => (0, request_1.resolvedQuery)(this.openlayerServerUrl, endpoint, args);
42
41
  /**
43
42
  * Streams data to the Openlayer inference pipeline.
44
- * @param {ChatCompletionData} data - The chat completion data to be streamed.
43
+ * @param {StreamingData} data - The chat completion data to be streamed.
45
44
  * @param {string} inferencePipelineId - The ID of the Openlayer inference pipeline to which data is streamed.
46
45
  * @returns {Promise<void>} A promise that resolves when the data has been successfully streamed.
47
46
  * @throws {Error} Throws an error if the Openlayer API key is not set or an error occurs in the streaming process.
48
47
  */
49
- this.streamData = (data, inferencePipelineId) => __awaiter(this, void 0, void 0, function* () {
48
+ this.streamData = (data, config, inferencePipelineId) => __awaiter(this, void 0, void 0, function* () {
50
49
  var _a;
51
50
  if (!this.openlayerApiKey) {
52
51
  throw new Error('Openlayer API key are required for streaming data.');
@@ -56,7 +55,7 @@ class OpenlayerClient {
56
55
  const dataStreamQuery = this.resolvedQuery(dataStreamEndpoint);
57
56
  const response = yield fetch(dataStreamQuery, {
58
57
  body: JSON.stringify({
59
- config: this.openlayerDefaultDataConfig,
58
+ config,
60
59
  rows: [
61
60
  Object.assign(Object.assign({}, data), { id: (0, uuid_1.v4)(), timestamp: Math.round(((_a = data.timestamp) !== null && _a !== void 0 ? _a : Date.now()) / 1000) }),
62
61
  ],
@@ -125,7 +124,7 @@ class OpenlayerClient {
125
124
  catch (_c) {
126
125
  const projectsEndpoint = '/projects';
127
126
  const projectsQuery = this.resolvedQuery(projectsEndpoint);
128
- const projectsResponse = yield fetch(projectsQuery, {
127
+ const response = yield fetch(projectsQuery, {
129
128
  body: JSON.stringify({
130
129
  description,
131
130
  name,
@@ -137,9 +136,10 @@ class OpenlayerClient {
137
136
  },
138
137
  method: 'POST',
139
138
  });
140
- const { items: projects } = yield projectsResponse.json();
139
+ const data = yield response.json();
140
+ const { items: projects, error } = data;
141
141
  if (!Array.isArray(projects)) {
142
- throw new Error('Invalid response from Openlayer');
142
+ throw new Error(typeof error === 'string' ? error : 'Invalid response from Openlayer');
143
143
  }
144
144
  const project = projects.find((p) => p.name === name);
145
145
  if (!(project === null || project === void 0 ? void 0 : project.id)) {
@@ -191,16 +191,17 @@ class OpenlayerClient {
191
191
  version: this.version,
192
192
  };
193
193
  const projectsQuery = this.resolvedQuery(projectsEndpoint, projectsQueryParameters);
194
- const projectsResponse = yield fetch(projectsQuery, {
194
+ const response = yield fetch(projectsQuery, {
195
195
  headers: {
196
196
  Authorization: `Bearer ${this.openlayerApiKey}`,
197
197
  'Content-Type': 'application/json',
198
198
  },
199
199
  method: 'GET',
200
200
  });
201
- const { items: projects } = yield projectsResponse.json();
201
+ const data = yield response.json();
202
+ const { items: projects, error } = data;
202
203
  if (!Array.isArray(projects)) {
203
- throw new Error('Invalid response from Openlayer');
204
+ throw new Error(typeof error === 'string' ? error : 'Invalid response from Openlayer');
204
205
  }
205
206
  const project = projects.find((p) => p.name === name);
206
207
  if (!(project === null || project === void 0 ? void 0 : project.id)) {
@@ -226,11 +227,9 @@ class OpenAIMonitor {
226
227
  constructor({ openAiApiKey, openlayerApiKey, openlayerProjectName, openlayerInferencePipelineName, openlayerServerUrl, }) {
227
228
  this.openlayerInferencePipelineName = 'production';
228
229
  this.monitoringOn = false;
229
- this.formatChatCompletionInput = (messages) => messages
230
- .filter(({ role }) => role === 'user')
231
- .map(({ content }) => content)
232
- .join('\n')
233
- .trim();
230
+ this.formatChatCompletionInput = (messages) => messages.map(({ content, role }, i) => (role === 'user'
231
+ ? `{{ message_${i} }}`
232
+ : content));
234
233
  /**
235
234
  * Creates a chat completion using the OpenAI client and streams the result to Openlayer.
236
235
  * @param {ChatCompletionCreateParams} body - The parameters for creating a chat completion.
@@ -251,6 +250,15 @@ class OpenAIMonitor {
251
250
  // Accumulate output for streamed responses
252
251
  let outputData = '';
253
252
  const response = yield this.openAIClient.chat.completions.create(body, options);
253
+ const prompt = this.formatChatCompletionInput(body.messages);
254
+ const inputVariableNames = prompt
255
+ .filter(({ role }) => role === 'user')
256
+ .map(({ content }) => content);
257
+ const inputVariables = body.messages
258
+ .filter(({ role }) => role === 'user')
259
+ .map(({ content }) => content);
260
+ const inputVariablesMap = inputVariableNames.reduce((acc, name, i) => (Object.assign(Object.assign({}, acc), { [name]: inputVariables[i] })), {});
261
+ const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames });
254
262
  if (body.stream) {
255
263
  const streamedResponse = response;
256
264
  try {
@@ -271,12 +279,7 @@ class OpenAIMonitor {
271
279
  }
272
280
  const endTime = Date.now();
273
281
  const latency = endTime - startTime;
274
- this.openlayerClient.streamData({
275
- input: this.formatChatCompletionInput(body.messages),
276
- latency,
277
- output: outputData,
278
- timestamp: startTime,
279
- }, inferencePipeline.id);
282
+ this.openlayerClient.streamData(Object.assign({ latency, output: outputData, prompt, timestamp: startTime }, inputVariablesMap), config, inferencePipeline.id);
280
283
  }
281
284
  else {
282
285
  const nonStreamedResponse = response;
@@ -287,13 +290,9 @@ class OpenAIMonitor {
287
290
  if (typeof output !== 'string') {
288
291
  throw new Error('No output received from OpenAI.');
289
292
  }
290
- this.openlayerClient.streamData({
291
- input: this.formatChatCompletionInput(body.messages),
292
- latency,
293
+ this.openlayerClient.streamData(Object.assign({ latency,
293
294
  output,
294
- timestamp: startTime,
295
- tokens: (_e = (_d = nonStreamedResponse.usage) === null || _d === void 0 ? void 0 : _d.total_tokens) !== null && _e !== void 0 ? _e : 0,
296
- }, inferencePipeline.id);
295
+ prompt, timestamp: startTime, tokens: (_e = (_d = nonStreamedResponse.usage) === null || _d === void 0 ? void 0 : _d.total_tokens) !== null && _e !== void 0 ? _e : 0 }, inputVariablesMap), config, inferencePipeline.id);
297
296
  }
298
297
  return response;
299
298
  });
@@ -321,6 +320,7 @@ class OpenAIMonitor {
321
320
  let outputData = '';
322
321
  let tokensData = 0;
323
322
  const response = yield this.openAIClient.completions.create(body, options);
323
+ const config = Object.assign(Object.assign({}, this.openlayerClient.defaultConfig), { inputVariableNames: ['input'] });
324
324
  if (body.stream) {
325
325
  const streamedResponse = response;
326
326
  try {
@@ -348,7 +348,7 @@ class OpenAIMonitor {
348
348
  output: outputData,
349
349
  timestamp: startTime,
350
350
  tokens: tokensData,
351
- }, inferencePipeline.id);
351
+ }, config, inferencePipeline.id);
352
352
  }
353
353
  else {
354
354
  const nonStreamedResponse = response;
@@ -361,7 +361,7 @@ class OpenAIMonitor {
361
361
  output: nonStreamedResponse.choices[0].text,
362
362
  timestamp: startTime,
363
363
  tokens: (_o = (_m = nonStreamedResponse.usage) === null || _m === void 0 ? void 0 : _m.total_tokens) !== null && _o !== void 0 ? _o : 0,
364
- }, inferencePipeline.id);
364
+ }, config, inferencePipeline.id);
365
365
  }
366
366
  return response;
367
367
  });
@@ -0,0 +1,45 @@
1
+ /*
2
+ * This example shows how to use Openlayer to monitor your LangChain workflows.
3
+ */
4
+
5
+ import { ChatOpenAI } from 'langchain/chat_models/openai';
6
+ import { OpenlayerClient } from 'openlayer';
7
+
8
+ // Instantiate the Openlayer client with your API key
9
+ const openlayer = new OpenlayerClient({
10
+ openlayerApiKey: 'YOUR_OPENLAYER_API_KEY',
11
+ });
12
+
13
+ // Create or load your project
14
+ const project = await openlayer.createProject('YOUR_PROJECT_NAME', 'llm-base');
15
+
16
+ /*
17
+ * Create or load an inference pipeline from your project.
18
+ * If no name is specified, it will default to 'production'
19
+ */
20
+ const inferencePipeline = await openlayer.createInferencePipeline(project.id);
21
+ const chatModel = new ChatOpenAI();
22
+ const inputs = [
23
+ 'What is the meaning of life?',
24
+ 'What would be a good name for a company that makes colorful socks?',
25
+ ];
26
+
27
+ await Promise.all(
28
+ inputs.map(async (input) => {
29
+ // Call the LLM
30
+ const output = await chatModel.predict(input);
31
+
32
+ // Stream the results to Openlayer
33
+ await openlayer.streamData(
34
+ {
35
+ input,
36
+ output,
37
+ },
38
+ {
39
+ ...openlayer.defaultConfig,
40
+ inputVariableNames: ['input'],
41
+ },
42
+ inferencePipeline.id
43
+ );
44
+ })
45
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openlayer",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "The Openlayer TypeScript client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
Binary file