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 +35 -0
- package/dist/index.d.ts +44 -11
- package/dist/index.js +30 -30
- package/examples/langchain.mjs +45 -0
- package/package.json +1 -1
- package/static/logo.png +0 -0
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
|
+
[](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
|
|
8
|
-
[
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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.
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
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
package/static/logo.png
ADDED
|
Binary file
|