openlayer 0.1.4 → 0.1.6
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 +187 -15
- package/dist/index.js +223 -108
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,35 +1,207 @@
|
|
|
1
1
|
import { RequestOptions } from 'openai/core';
|
|
2
2
|
import { ChatCompletion, ChatCompletionChunk, ChatCompletionCreateParams, Completion, CompletionCreateParams } from 'openai/resources';
|
|
3
3
|
import { Stream } from 'openai/streaming';
|
|
4
|
+
/**
|
|
5
|
+
* Represents the data structure for a chat completion.
|
|
6
|
+
*/
|
|
4
7
|
export interface ChatCompletionData {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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[][];
|
|
14
|
+
/**
|
|
15
|
+
* The latency of the chat completion in milliseconds. Optional.
|
|
16
|
+
*/
|
|
17
|
+
latency?: number;
|
|
18
|
+
/**
|
|
19
|
+
* The output string generated by the chat completion.
|
|
20
|
+
*/
|
|
21
|
+
output: string;
|
|
22
|
+
/**
|
|
23
|
+
* A timestamp representing when the chat completion occurred. Optional.
|
|
24
|
+
*/
|
|
25
|
+
timestamp?: number;
|
|
26
|
+
/**
|
|
27
|
+
* The number of tokens used in the chat completion. Optional.
|
|
28
|
+
*/
|
|
9
29
|
tokens?: number;
|
|
10
30
|
}
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Configuration settings for uploading chat completion data to Openlayer.
|
|
33
|
+
*/
|
|
34
|
+
export interface ChatCompletionConfig {
|
|
35
|
+
/**
|
|
36
|
+
* The name of the column that stores the ground truth data. Can be null.
|
|
37
|
+
*/
|
|
38
|
+
groundTruthColumnName: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* The name of the column that stores inference IDs. Can be null.
|
|
41
|
+
*/
|
|
42
|
+
inferenceIdColumnName: string | null;
|
|
43
|
+
/**
|
|
44
|
+
* An array of names for input variable columns. Can be null.
|
|
45
|
+
*/
|
|
46
|
+
inputVariableNames: string[] | null;
|
|
47
|
+
/**
|
|
48
|
+
* The name of the column that stores latency data. Can be null.
|
|
49
|
+
*/
|
|
50
|
+
latencyColumnName: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* The name of the column that stores the number of tokens. Can be null.
|
|
53
|
+
*/
|
|
54
|
+
numOfTokenColumnName: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* The name of the column that stores output data. Can be null.
|
|
57
|
+
*/
|
|
58
|
+
outputColumnName: string | null;
|
|
59
|
+
/**
|
|
60
|
+
* The name of the column that stores timestamp data. Can be null.
|
|
61
|
+
*/
|
|
62
|
+
timestampColumnName: string | null;
|
|
63
|
+
}
|
|
64
|
+
type OpenlayerClientConstructorProps = {
|
|
13
65
|
openlayerApiKey?: string;
|
|
14
66
|
openlayerInferencePipelineName?: string;
|
|
15
67
|
openlayerProjectName?: string;
|
|
16
68
|
openlayerServerUrl?: string;
|
|
17
69
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
70
|
+
type OpenAIMonitorConstructorProps = OpenlayerClientConstructorProps & {
|
|
71
|
+
openAiApiKey: string;
|
|
72
|
+
};
|
|
73
|
+
type OpenlayerInferencePipeline = {
|
|
74
|
+
dataVolumeGraphs?: OpenlayerSampleVolumeGraph;
|
|
75
|
+
dateCreated: string;
|
|
76
|
+
dateLastEvaluated?: string;
|
|
77
|
+
dateLastSampleReceived?: string;
|
|
78
|
+
dateOfNextEvaluation?: string;
|
|
79
|
+
dateUpdated: string;
|
|
80
|
+
description?: string;
|
|
81
|
+
failingGoalCount: number;
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
passingGoalCount: number;
|
|
85
|
+
projectId: string;
|
|
86
|
+
status: OpenlayerInferencePipelineStatus;
|
|
87
|
+
statusMessage?: string;
|
|
88
|
+
totalGoalCount: number;
|
|
89
|
+
};
|
|
90
|
+
type OpenlayerInferencePipelineStatus = 'completed' | 'failed' | 'paused' | 'queued' | 'running' | 'unknown';
|
|
91
|
+
type OpenlayerProject = {
|
|
92
|
+
dateCreated: string;
|
|
93
|
+
dateUpdated: string;
|
|
94
|
+
description?: string;
|
|
95
|
+
developmentGoalCount: number;
|
|
96
|
+
goalCount: number;
|
|
97
|
+
id: string;
|
|
98
|
+
inferencePipelineCount: number;
|
|
99
|
+
memberIds: string[];
|
|
100
|
+
monitoringGoalCount: number;
|
|
101
|
+
name: string;
|
|
102
|
+
sample?: boolean;
|
|
103
|
+
slackChannelId?: string;
|
|
104
|
+
slackChannelName?: string;
|
|
105
|
+
slackChannelNotificationsEnabled: boolean;
|
|
106
|
+
taskType: OpenlayerTaskType;
|
|
107
|
+
unreadNotificationCount: number;
|
|
108
|
+
versionCount: number;
|
|
109
|
+
};
|
|
110
|
+
type OpenlayerSampleVolumeGraphBucket = {
|
|
111
|
+
title: string;
|
|
112
|
+
xAxis: {
|
|
113
|
+
data: string[];
|
|
114
|
+
title: string;
|
|
115
|
+
};
|
|
116
|
+
yAxis: {
|
|
117
|
+
data: number[];
|
|
118
|
+
title: string;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
type OpenlayerSampleVolumeGraph = {
|
|
122
|
+
daily: OpenlayerSampleVolumeGraphBucket;
|
|
123
|
+
hourly: OpenlayerSampleVolumeGraphBucket;
|
|
124
|
+
monthly: OpenlayerSampleVolumeGraphBucket;
|
|
125
|
+
weekly: OpenlayerSampleVolumeGraphBucket;
|
|
126
|
+
};
|
|
127
|
+
type OpenlayerTaskType = 'llm-base' | 'tabular-classification' | 'tabular-regression' | 'text-classification';
|
|
128
|
+
declare class OpenlayerClient {
|
|
21
129
|
private openlayerApiKey?;
|
|
22
130
|
private openlayerProjectName?;
|
|
23
131
|
private openlayerInferencePipelineName?;
|
|
24
132
|
private openlayerServerUrl;
|
|
25
133
|
private version;
|
|
26
|
-
constructor({
|
|
27
|
-
private formatChatCompletionInput;
|
|
134
|
+
constructor({ openlayerApiKey, openlayerInferencePipelineName, openlayerProjectName, openlayerServerUrl, }: OpenlayerClientConstructorProps);
|
|
28
135
|
private resolvedQuery;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
136
|
+
private uploadToInferencePipeline;
|
|
137
|
+
/**
|
|
138
|
+
* Uploads data to Openlayer. This function takes ChatCompletionData and an optional
|
|
139
|
+
* ChatCompletionConfig, then uploads the data to the specified Openlayer Inference Pipeline.
|
|
140
|
+
* @param data The ChatCompletionData to be uploaded.
|
|
141
|
+
* @param config Configuration for the data upload.
|
|
142
|
+
* @throws Throws an error if Openlayer API key or project name are not set.
|
|
143
|
+
* @returns A promise that resolves when the data has been successfully uploaded.
|
|
144
|
+
*/
|
|
145
|
+
uploadDataToOpenlayer: (data: ChatCompletionData, config: ChatCompletionConfig) => Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Creates a new inference pipeline in Openlayer, or loads an existing one if it already exists.
|
|
148
|
+
* @param name The name of the inference pipeline.
|
|
149
|
+
* @param projectId The project ID containing the inference pipeline.
|
|
150
|
+
* @throws Throws an error if the inference pipeline cannot be created or found.
|
|
151
|
+
* @returns A promise that resolves to an OpenlayerInferencePipeline object.
|
|
152
|
+
*/
|
|
153
|
+
createInferencePipeline: (name: string, projectId: string) => Promise<OpenlayerInferencePipeline>;
|
|
154
|
+
/**
|
|
155
|
+
* Creates a new project in Openlayer, or loads an existing one if it already exists.
|
|
156
|
+
* @param name The name of the project.
|
|
157
|
+
* @param taskType The type of task associated with the project.
|
|
158
|
+
* @param description Optional description of the project.
|
|
159
|
+
* @throws Throws an error if the project cannot be created or found.
|
|
160
|
+
* @returns A promise that resolves to an OpenlayerProject object.
|
|
161
|
+
*/
|
|
162
|
+
createProject: (name: string, taskType: OpenlayerTaskType, description?: string) => Promise<OpenlayerProject>;
|
|
163
|
+
/**
|
|
164
|
+
* Loads an existing inference pipeline from Openlayer based on its name and project ID.
|
|
165
|
+
* @param name The name of the inference pipeline.
|
|
166
|
+
* @param projectId The project ID containing the inference pipeline.
|
|
167
|
+
* @throws Throws an error if the inference pipeline is not found.
|
|
168
|
+
* @returns A promise that resolves to an OpenlayerInferencePipeline object.
|
|
169
|
+
*/
|
|
170
|
+
loadInferencePipeline: (name: string, projectId: string) => Promise<OpenlayerInferencePipeline>;
|
|
171
|
+
loadProject: (name: string) => Promise<OpenlayerProject>;
|
|
172
|
+
}
|
|
173
|
+
export declare class OpenAIMonitor {
|
|
174
|
+
private openlayerClient;
|
|
175
|
+
private openAIClient;
|
|
176
|
+
private openlayerDefaultDataConfig;
|
|
177
|
+
private monitoringOn;
|
|
178
|
+
constructor({ openAiApiKey, openlayerApiKey, openlayerInferencePipelineName, openlayerProjectName, openlayerServerUrl, }: OpenAIMonitorConstructorProps);
|
|
179
|
+
private formatChatCompletionInput;
|
|
180
|
+
/**
|
|
181
|
+
* Creates a new ChatCompletion instance. If monitoring is not active, an error is thrown.
|
|
182
|
+
* This function also measures latency and uploads data to Openlayer.
|
|
183
|
+
* @param body The parameters for creating a chat completion.
|
|
184
|
+
* @param options Optional request options.
|
|
185
|
+
* @throws Throws an error if monitoring is not active or if there is no output received from OpenAI.
|
|
186
|
+
* @returns A promise that resolves to a ChatCompletion or a Stream of ChatCompletionChunks.
|
|
187
|
+
*/
|
|
32
188
|
createChatCompletion: (body: ChatCompletionCreateParams, options?: RequestOptions) => Promise<ChatCompletion | Stream<ChatCompletionChunk>>;
|
|
189
|
+
/**
|
|
190
|
+
* Creates a new Completion instance. If monitoring is not active, an error is thrown.
|
|
191
|
+
* This function also measures latency and uploads data to Openlayer.
|
|
192
|
+
* @param body The parameters for creating a completion.
|
|
193
|
+
* @param options Optional request options.
|
|
194
|
+
* @throws Throws an error if monitoring is not active or if no prompt is provided.
|
|
195
|
+
* @returns A promise that resolves to a Completion or a Stream of Completions.
|
|
196
|
+
*/
|
|
33
197
|
createCompletion: (body: CompletionCreateParams, options?: RequestOptions) => Promise<Completion | Stream<Completion>>;
|
|
198
|
+
/**
|
|
199
|
+
* Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
|
|
200
|
+
*/
|
|
201
|
+
startMonitoring(): void;
|
|
202
|
+
/**
|
|
203
|
+
* Stops monitoring for the OpenAI Monitor instance. If monitoring is not active, a warning is logged.
|
|
204
|
+
*/
|
|
205
|
+
stopMonitoring(): void;
|
|
34
206
|
}
|
|
35
|
-
export default
|
|
207
|
+
export default OpenlayerClient;
|
package/dist/index.js
CHANGED
|
@@ -16,38 +16,80 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
16
16
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
17
|
};
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.OpenAIMonitor = void 0;
|
|
19
20
|
const openai_1 = require("openai");
|
|
20
21
|
const uuid_1 = require("uuid");
|
|
21
22
|
const request_1 = require("./utils/request");
|
|
22
|
-
class
|
|
23
|
-
constructor({
|
|
24
|
-
this.monitoringOn = false;
|
|
23
|
+
class OpenlayerClient {
|
|
24
|
+
constructor({ openlayerApiKey, openlayerInferencePipelineName, openlayerProjectName, openlayerServerUrl, }) {
|
|
25
25
|
this.openlayerServerUrl = 'https://api.openlayer.com/v1';
|
|
26
26
|
this.version = '0.1.0a16';
|
|
27
|
-
this.formatChatCompletionInput = (messages) => messages
|
|
28
|
-
.filter(({ role }) => role === 'user')
|
|
29
|
-
.map(({ content }) => content)
|
|
30
|
-
.join('\n')
|
|
31
|
-
.trim();
|
|
32
27
|
this.resolvedQuery = (endpoint, args = {}) => (0, request_1.resolvedQuery)(this.openlayerServerUrl, endpoint, args);
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
this.uploadToInferencePipeline = (inferencePipelineId, data, config) => __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
var _a;
|
|
30
|
+
const dataStreamEndpoint = `/inference-pipelines/${inferencePipelineId}/data-stream`;
|
|
31
|
+
const dataStreamQuery = this.resolvedQuery(dataStreamEndpoint);
|
|
32
|
+
const response = yield fetch(dataStreamQuery, {
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
config,
|
|
35
|
+
rows: [
|
|
36
|
+
Object.assign(Object.assign({}, data), { id: (0, uuid_1.v4)(), timestamp: Math.round(((_a = data.timestamp) !== null && _a !== void 0 ? _a : Date.now()) / 1000) }),
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
39
|
+
headers: {
|
|
40
|
+
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
},
|
|
43
|
+
method: 'POST',
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
console.error('Error making POST request:', response.status);
|
|
47
|
+
throw new Error(`Error: ${response.status}`);
|
|
48
|
+
}
|
|
49
|
+
return yield response.json();
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Uploads data to Openlayer. This function takes ChatCompletionData and an optional
|
|
53
|
+
* ChatCompletionConfig, then uploads the data to the specified Openlayer Inference Pipeline.
|
|
54
|
+
* @param data The ChatCompletionData to be uploaded.
|
|
55
|
+
* @param config Configuration for the data upload.
|
|
56
|
+
* @throws Throws an error if Openlayer API key or project name are not set.
|
|
57
|
+
* @returns A promise that resolves when the data has been successfully uploaded.
|
|
58
|
+
*/
|
|
59
|
+
this.uploadDataToOpenlayer = (data, config) => __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
if (!this.openlayerApiKey || !this.openlayerProjectName) {
|
|
61
|
+
throw new Error('Openlayer API key and project name are required for publishing.');
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const project = yield this.createProject(this.openlayerProjectName, 'llm-base');
|
|
65
|
+
const inferencePipeline = yield this.createInferencePipeline(this.openlayerProjectName, project.id);
|
|
66
|
+
yield this.uploadToInferencePipeline(inferencePipeline.id, data, config);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('Error publishing data to Openlayer:', error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Creates a new inference pipeline in Openlayer, or loads an existing one if it already exists.
|
|
75
|
+
* @param name The name of the inference pipeline.
|
|
76
|
+
* @param projectId The project ID containing the inference pipeline.
|
|
77
|
+
* @throws Throws an error if the inference pipeline cannot be created or found.
|
|
78
|
+
* @returns A promise that resolves to an OpenlayerInferencePipeline object.
|
|
79
|
+
*/
|
|
80
|
+
this.createInferencePipeline = (name, projectId) => __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
try {
|
|
82
|
+
return yield this.loadInferencePipeline(name, projectId);
|
|
83
|
+
}
|
|
84
|
+
catch (_b) {
|
|
85
|
+
const createInferencePipelineEndpoint = `/projects/${projectId}/inference-pipelines`;
|
|
86
|
+
const createInferencePipelineQuery = this.resolvedQuery(createInferencePipelineEndpoint, { version: this.version });
|
|
87
|
+
const createInferencePipelineResponse = yield fetch(createInferencePipelineQuery, {
|
|
38
88
|
body: JSON.stringify({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
latencyColumnName: 'latency',
|
|
44
|
-
numOfTokenColumnName: 'tokens',
|
|
45
|
-
outputColumnName: 'output',
|
|
46
|
-
timestampColumnName: 'timestamp',
|
|
47
|
-
},
|
|
48
|
-
rows: [
|
|
49
|
-
Object.assign(Object.assign({}, data), { id: (0, uuid_1.v4)(), timestamp: Math.round(data.timestamp / 1000) }),
|
|
50
|
-
],
|
|
89
|
+
description: '',
|
|
90
|
+
name: typeof this.openlayerInferencePipelineName === 'undefined'
|
|
91
|
+
? 'production'
|
|
92
|
+
: this.openlayerInferencePipelineName,
|
|
51
93
|
}),
|
|
52
94
|
headers: {
|
|
53
95
|
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
@@ -55,87 +97,141 @@ class OpenAIMonitor {
|
|
|
55
97
|
},
|
|
56
98
|
method: 'POST',
|
|
57
99
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
throw new Error(
|
|
100
|
+
const inferencePipeline = yield createInferencePipelineResponse.json();
|
|
101
|
+
if (!(inferencePipeline === null || inferencePipeline === void 0 ? void 0 : inferencePipeline.id)) {
|
|
102
|
+
throw new Error('Error creating inference pipeline');
|
|
61
103
|
}
|
|
62
|
-
return
|
|
63
|
-
});
|
|
64
|
-
if (!this.openlayerApiKey || !this.openlayerProjectName) {
|
|
65
|
-
throw new Error('Openlayer API key and project name are required for publishing.');
|
|
104
|
+
return inferencePipeline;
|
|
66
105
|
}
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new project in Openlayer, or loads an existing one if it already exists.
|
|
109
|
+
* @param name The name of the project.
|
|
110
|
+
* @param taskType The type of task associated with the project.
|
|
111
|
+
* @param description Optional description of the project.
|
|
112
|
+
* @throws Throws an error if the project cannot be created or found.
|
|
113
|
+
* @returns A promise that resolves to an OpenlayerProject object.
|
|
114
|
+
*/
|
|
115
|
+
this.createProject = (name, taskType, description) => __awaiter(this, void 0, void 0, function* () {
|
|
67
116
|
try {
|
|
117
|
+
return yield this.loadProject(name);
|
|
118
|
+
}
|
|
119
|
+
catch (_c) {
|
|
68
120
|
const projectsEndpoint = '/projects';
|
|
69
|
-
const
|
|
70
|
-
name: this.openlayerProjectName,
|
|
71
|
-
version: this.version,
|
|
72
|
-
};
|
|
73
|
-
const projectsQuery = this.resolvedQuery(projectsEndpoint, projectsQueryParameters);
|
|
121
|
+
const projectsQuery = this.resolvedQuery(projectsEndpoint);
|
|
74
122
|
const projectsResponse = yield fetch(projectsQuery, {
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
description,
|
|
125
|
+
name,
|
|
126
|
+
taskType,
|
|
127
|
+
}),
|
|
75
128
|
headers: {
|
|
76
129
|
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
77
130
|
'Content-Type': 'application/json',
|
|
78
131
|
},
|
|
79
|
-
method: '
|
|
132
|
+
method: 'POST',
|
|
80
133
|
});
|
|
81
134
|
const { items: projects } = yield projectsResponse.json();
|
|
82
135
|
if (!Array.isArray(projects)) {
|
|
83
136
|
throw new Error('Invalid response from Openlayer');
|
|
84
137
|
}
|
|
85
|
-
const project = projects.find((
|
|
138
|
+
const project = projects.find((p) => p.name === name);
|
|
86
139
|
if (!(project === null || project === void 0 ? void 0 : project.id)) {
|
|
87
140
|
throw new Error('Project not found');
|
|
88
141
|
}
|
|
89
|
-
|
|
90
|
-
const inferencePipelineQueryParameters = {
|
|
91
|
-
name: this.openlayerInferencePipelineName,
|
|
92
|
-
version: this.version,
|
|
93
|
-
};
|
|
94
|
-
const inferencePipelineQuery = this.resolvedQuery(inferencePipelineEndpoint, inferencePipelineQueryParameters);
|
|
95
|
-
const inferencePipelineResponse = yield fetch(inferencePipelineQuery, {
|
|
96
|
-
headers: {
|
|
97
|
-
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
98
|
-
'Content-Type': 'application/json',
|
|
99
|
-
},
|
|
100
|
-
method: 'GET',
|
|
101
|
-
});
|
|
102
|
-
const { items: inferencePipelines } = yield inferencePipelineResponse.json();
|
|
103
|
-
const inferencePipeline = Array.isArray(inferencePipelines)
|
|
104
|
-
? inferencePipelines.find(({ name }) => typeof this.openlayerInferencePipelineName === 'undefined' ||
|
|
105
|
-
name === this.openlayerInferencePipelineName)
|
|
106
|
-
: undefined;
|
|
107
|
-
if (inferencePipeline === null || inferencePipeline === void 0 ? void 0 : inferencePipeline.id) {
|
|
108
|
-
const { id: inferencePipelineId } = inferencePipeline;
|
|
109
|
-
yield uploadToInferencePipeline(inferencePipelineId);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
const createInferencePipelineEndpoint = `/projects/${project.id}/inference-pipelines`;
|
|
113
|
-
const createInferencePipelineQuery = this.resolvedQuery(createInferencePipelineEndpoint, { version: this.version });
|
|
114
|
-
const createInferencePipelineResponse = yield fetch(createInferencePipelineQuery, {
|
|
115
|
-
body: JSON.stringify({
|
|
116
|
-
description: '',
|
|
117
|
-
name: typeof this.openlayerInferencePipelineName === 'undefined'
|
|
118
|
-
? 'production'
|
|
119
|
-
: this.openlayerInferencePipelineName,
|
|
120
|
-
}),
|
|
121
|
-
headers: {
|
|
122
|
-
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
123
|
-
'Content-Type': 'application/json',
|
|
124
|
-
},
|
|
125
|
-
method: 'POST',
|
|
126
|
-
});
|
|
127
|
-
const { id: inferencePipelineId } = yield createInferencePipelineResponse.json();
|
|
128
|
-
if (!inferencePipelineId) {
|
|
129
|
-
throw new Error('Error creating inference pipeline');
|
|
130
|
-
}
|
|
131
|
-
yield uploadToInferencePipeline(inferencePipelineId);
|
|
132
|
-
}
|
|
142
|
+
return project;
|
|
133
143
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* Loads an existing inference pipeline from Openlayer based on its name and project ID.
|
|
147
|
+
* @param name The name of the inference pipeline.
|
|
148
|
+
* @param projectId The project ID containing the inference pipeline.
|
|
149
|
+
* @throws Throws an error if the inference pipeline is not found.
|
|
150
|
+
* @returns A promise that resolves to an OpenlayerInferencePipeline object.
|
|
151
|
+
*/
|
|
152
|
+
this.loadInferencePipeline = (name, projectId) => __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const inferencePipelineEndpoint = `/projects/${projectId}/inference-pipelines`;
|
|
154
|
+
const inferencePipelineQueryParameters = {
|
|
155
|
+
name: this.openlayerInferencePipelineName,
|
|
156
|
+
version: this.version,
|
|
157
|
+
};
|
|
158
|
+
const inferencePipelineQuery = this.resolvedQuery(inferencePipelineEndpoint, inferencePipelineQueryParameters);
|
|
159
|
+
const inferencePipelineResponse = yield fetch(inferencePipelineQuery, {
|
|
160
|
+
headers: {
|
|
161
|
+
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
162
|
+
'Content-Type': 'application/json',
|
|
163
|
+
},
|
|
164
|
+
method: 'GET',
|
|
165
|
+
});
|
|
166
|
+
const { items: inferencePipelines } = yield inferencePipelineResponse.json();
|
|
167
|
+
const inferencePipeline = Array.isArray(inferencePipelines)
|
|
168
|
+
? inferencePipelines.find((p) => p.name === name)
|
|
169
|
+
: undefined;
|
|
170
|
+
if (!(inferencePipeline === null || inferencePipeline === void 0 ? void 0 : inferencePipeline.id)) {
|
|
171
|
+
throw new Error('Inference pipeline not found');
|
|
172
|
+
}
|
|
173
|
+
return inferencePipeline;
|
|
174
|
+
});
|
|
175
|
+
this.loadProject = (name) => __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
const projectsEndpoint = '/projects';
|
|
177
|
+
const projectsQueryParameters = {
|
|
178
|
+
name,
|
|
179
|
+
version: this.version,
|
|
180
|
+
};
|
|
181
|
+
const projectsQuery = this.resolvedQuery(projectsEndpoint, projectsQueryParameters);
|
|
182
|
+
const projectsResponse = yield fetch(projectsQuery, {
|
|
183
|
+
headers: {
|
|
184
|
+
Authorization: `Bearer ${this.openlayerApiKey}`,
|
|
185
|
+
'Content-Type': 'application/json',
|
|
186
|
+
},
|
|
187
|
+
method: 'GET',
|
|
188
|
+
});
|
|
189
|
+
const { items: projects } = yield projectsResponse.json();
|
|
190
|
+
if (!Array.isArray(projects)) {
|
|
191
|
+
throw new Error('Invalid response from Openlayer');
|
|
192
|
+
}
|
|
193
|
+
const project = projects.find((p) => p.name === name);
|
|
194
|
+
if (!(project === null || project === void 0 ? void 0 : project.id)) {
|
|
195
|
+
throw new Error('Project not found');
|
|
137
196
|
}
|
|
197
|
+
return project;
|
|
138
198
|
});
|
|
199
|
+
this.openlayerApiKey = openlayerApiKey;
|
|
200
|
+
this.openlayerInferencePipelineName = openlayerInferencePipelineName;
|
|
201
|
+
this.openlayerProjectName = openlayerProjectName;
|
|
202
|
+
if (openlayerServerUrl) {
|
|
203
|
+
this.openlayerServerUrl = openlayerServerUrl;
|
|
204
|
+
}
|
|
205
|
+
if (!this.openlayerApiKey || !this.openlayerProjectName) {
|
|
206
|
+
throw new Error('Openlayer API key and project name are required for publishing.');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
class OpenAIMonitor {
|
|
211
|
+
constructor({ openAiApiKey, openlayerApiKey, openlayerInferencePipelineName, openlayerProjectName, openlayerServerUrl, }) {
|
|
212
|
+
this.openlayerDefaultDataConfig = {
|
|
213
|
+
groundTruthColumnName: null,
|
|
214
|
+
inferenceIdColumnName: 'id',
|
|
215
|
+
inputVariableNames: ['input'],
|
|
216
|
+
latencyColumnName: 'latency',
|
|
217
|
+
numOfTokenColumnName: 'tokens',
|
|
218
|
+
outputColumnName: 'output',
|
|
219
|
+
timestampColumnName: 'timestamp',
|
|
220
|
+
};
|
|
221
|
+
this.monitoringOn = false;
|
|
222
|
+
this.formatChatCompletionInput = (messages) => messages
|
|
223
|
+
.filter(({ role }) => role === 'user')
|
|
224
|
+
.map(({ content }) => content)
|
|
225
|
+
.join('\n')
|
|
226
|
+
.trim();
|
|
227
|
+
/**
|
|
228
|
+
* Creates a new ChatCompletion instance. If monitoring is not active, an error is thrown.
|
|
229
|
+
* This function also measures latency and uploads data to Openlayer.
|
|
230
|
+
* @param body The parameters for creating a chat completion.
|
|
231
|
+
* @param options Optional request options.
|
|
232
|
+
* @throws Throws an error if monitoring is not active or if there is no output received from OpenAI.
|
|
233
|
+
* @returns A promise that resolves to a ChatCompletion or a Stream of ChatCompletionChunks.
|
|
234
|
+
*/
|
|
139
235
|
this.createChatCompletion = (body, options) => __awaiter(this, void 0, void 0, function* () {
|
|
140
236
|
var _a, e_1, _b, _c;
|
|
141
237
|
var _d, _e;
|
|
@@ -146,7 +242,7 @@ class OpenAIMonitor {
|
|
|
146
242
|
const startTime = Date.now();
|
|
147
243
|
// Accumulate output for streamed responses
|
|
148
244
|
let outputData = '';
|
|
149
|
-
const response = yield this.
|
|
245
|
+
const response = yield this.openAIClient.chat.completions.create(body, options);
|
|
150
246
|
if (body.stream) {
|
|
151
247
|
const streamedResponse = response;
|
|
152
248
|
try {
|
|
@@ -167,40 +263,55 @@ class OpenAIMonitor {
|
|
|
167
263
|
}
|
|
168
264
|
const endTime = Date.now();
|
|
169
265
|
const latency = endTime - startTime;
|
|
170
|
-
this.uploadDataToOpenlayer({
|
|
266
|
+
this.openlayerClient.uploadDataToOpenlayer({
|
|
171
267
|
input: this.formatChatCompletionInput(body.messages),
|
|
172
268
|
latency,
|
|
173
269
|
output: outputData,
|
|
174
270
|
timestamp: startTime,
|
|
175
|
-
});
|
|
271
|
+
}, this.openlayerDefaultDataConfig);
|
|
176
272
|
}
|
|
177
273
|
else {
|
|
178
274
|
const nonStreamedResponse = response;
|
|
179
275
|
// Handle regular (non-streamed) response
|
|
180
276
|
const endTime = Date.now();
|
|
181
277
|
const latency = endTime - startTime;
|
|
182
|
-
|
|
278
|
+
const output = nonStreamedResponse.choices[0].message.content;
|
|
279
|
+
if (typeof output !== 'string') {
|
|
280
|
+
throw new Error('No output received from OpenAI.');
|
|
281
|
+
}
|
|
282
|
+
this.openlayerClient.uploadDataToOpenlayer({
|
|
183
283
|
input: this.formatChatCompletionInput(body.messages),
|
|
184
284
|
latency,
|
|
185
|
-
output
|
|
285
|
+
output,
|
|
186
286
|
timestamp: startTime,
|
|
187
287
|
tokens: (_e = (_d = nonStreamedResponse.usage) === null || _d === void 0 ? void 0 : _d.total_tokens) !== null && _e !== void 0 ? _e : 0,
|
|
188
|
-
});
|
|
288
|
+
}, this.openlayerDefaultDataConfig);
|
|
189
289
|
}
|
|
190
290
|
return response;
|
|
191
291
|
});
|
|
292
|
+
/**
|
|
293
|
+
* Creates a new Completion instance. If monitoring is not active, an error is thrown.
|
|
294
|
+
* This function also measures latency and uploads data to Openlayer.
|
|
295
|
+
* @param body The parameters for creating a completion.
|
|
296
|
+
* @param options Optional request options.
|
|
297
|
+
* @throws Throws an error if monitoring is not active or if no prompt is provided.
|
|
298
|
+
* @returns A promise that resolves to a Completion or a Stream of Completions.
|
|
299
|
+
*/
|
|
192
300
|
this.createCompletion = (body, options) => __awaiter(this, void 0, void 0, function* () {
|
|
193
301
|
var _g, e_2, _h, _j;
|
|
194
302
|
var _k, _l, _m, _o;
|
|
195
303
|
if (!this.monitoringOn) {
|
|
196
304
|
throw new Error('Monitoring is not active.');
|
|
197
305
|
}
|
|
306
|
+
if (!body.prompt) {
|
|
307
|
+
throw new Error('No prompt provided.');
|
|
308
|
+
}
|
|
198
309
|
// Start a timer to measure latency
|
|
199
310
|
const startTime = Date.now();
|
|
200
311
|
// Accumulate output and tokens data for streamed responses
|
|
201
312
|
let outputData = '';
|
|
202
313
|
let tokensData = 0;
|
|
203
|
-
const response = yield this.
|
|
314
|
+
const response = yield this.openAIClient.completions.create(body, options);
|
|
204
315
|
if (body.stream) {
|
|
205
316
|
const streamedResponse = response;
|
|
206
317
|
try {
|
|
@@ -222,43 +333,43 @@ class OpenAIMonitor {
|
|
|
222
333
|
}
|
|
223
334
|
const endTime = Date.now();
|
|
224
335
|
const latency = endTime - startTime;
|
|
225
|
-
this.uploadDataToOpenlayer({
|
|
336
|
+
this.openlayerClient.uploadDataToOpenlayer({
|
|
226
337
|
input: body.prompt,
|
|
227
338
|
latency,
|
|
228
339
|
output: outputData,
|
|
229
340
|
timestamp: startTime,
|
|
230
341
|
tokens: tokensData,
|
|
231
|
-
});
|
|
342
|
+
}, this.openlayerDefaultDataConfig);
|
|
232
343
|
}
|
|
233
344
|
else {
|
|
234
345
|
const nonStreamedResponse = response;
|
|
235
346
|
// Handle regular (non-streamed) response
|
|
236
347
|
const endTime = Date.now();
|
|
237
348
|
const latency = endTime - startTime;
|
|
238
|
-
this.uploadDataToOpenlayer({
|
|
349
|
+
this.openlayerClient.uploadDataToOpenlayer({
|
|
239
350
|
input: body.prompt,
|
|
240
351
|
latency,
|
|
241
352
|
output: nonStreamedResponse.choices[0].text,
|
|
242
353
|
timestamp: startTime,
|
|
243
354
|
tokens: (_o = (_m = nonStreamedResponse.usage) === null || _m === void 0 ? void 0 : _m.total_tokens) !== null && _o !== void 0 ? _o : 0,
|
|
244
|
-
});
|
|
355
|
+
}, this.openlayerDefaultDataConfig);
|
|
245
356
|
}
|
|
246
357
|
return response;
|
|
247
358
|
});
|
|
248
|
-
this.
|
|
359
|
+
this.openlayerClient = new OpenlayerClient({
|
|
360
|
+
openlayerApiKey,
|
|
361
|
+
openlayerInferencePipelineName,
|
|
362
|
+
openlayerProjectName,
|
|
363
|
+
openlayerServerUrl,
|
|
364
|
+
});
|
|
365
|
+
this.openAIClient = new openai_1.default({
|
|
249
366
|
apiKey: openAiApiKey,
|
|
250
367
|
dangerouslyAllowBrowser: true,
|
|
251
368
|
});
|
|
252
|
-
this.openlayerApiKey = openlayerApiKey;
|
|
253
|
-
this.openlayerInferencePipelineName = openlayerInferencePipelineName;
|
|
254
|
-
this.openlayerProjectName = openlayerProjectName;
|
|
255
|
-
if (openlayerServerUrl) {
|
|
256
|
-
this.openlayerServerUrl = openlayerServerUrl;
|
|
257
|
-
}
|
|
258
|
-
if (!this.openlayerApiKey || !this.openlayerProjectName) {
|
|
259
|
-
throw new Error('Openlayer API key and project name are required for publishing.');
|
|
260
|
-
}
|
|
261
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Starts monitoring for the OpenAI Monitor instance. If monitoring is already active, a warning is logged.
|
|
372
|
+
*/
|
|
262
373
|
startMonitoring() {
|
|
263
374
|
if (this.monitoringOn) {
|
|
264
375
|
console.warn('Monitoring is already on!');
|
|
@@ -267,6 +378,9 @@ class OpenAIMonitor {
|
|
|
267
378
|
this.monitoringOn = true;
|
|
268
379
|
console.info('Monitoring started.');
|
|
269
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Stops monitoring for the OpenAI Monitor instance. If monitoring is not active, a warning is logged.
|
|
383
|
+
*/
|
|
270
384
|
stopMonitoring() {
|
|
271
385
|
if (!this.monitoringOn) {
|
|
272
386
|
console.warn('Monitoring is not active.');
|
|
@@ -276,4 +390,5 @@ class OpenAIMonitor {
|
|
|
276
390
|
console.info('Monitoring stopped.');
|
|
277
391
|
}
|
|
278
392
|
}
|
|
279
|
-
exports.
|
|
393
|
+
exports.OpenAIMonitor = OpenAIMonitor;
|
|
394
|
+
exports.default = OpenlayerClient;
|