openpipe
Version:
OpenPipe TypeScript SDK: Fine-Tuning, Inference, and Metrics for Production Apps
1,264 lines (1,245 loc) • 38.5 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
// src/index.ts
var index_exports = {};
__export(index_exports, {
openai: () => openai_exports
});
module.exports = __toCommonJS(index_exports);
// src/openai.ts
var openai_exports = {};
__export(openai_exports, {
default: () => OpenAI2
});
var openai = __toESM(require("openai"), 1);
// src/openai/streaming.ts
var import_streaming = require("openai/streaming");
// src/openai/mergeChunks.ts
var omit = (obj, ...keys) => {
const ret = { ...obj };
for (const key of keys) {
delete ret[key];
}
return ret;
};
function mergeChunks(base, chunk) {
if (base === null) {
return mergeChunks(
{ ...chunk, object: "chat.completion", choices: [], usage: chunk.usage ?? void 0 },
// Prevent function call and tool call arguments from being double-merged
{
...chunk,
choices: chunk.choices.map((c) => ({
...c,
delta: {
...c.delta,
function_call: c.delta.function_call ? {
...c.delta.function_call
} : void 0,
tool_calls: c.delta.tool_calls?.map((tc) => ({
...tc,
function: {
...tc.function
}
}))
}
}))
}
);
}
const choices = [...base.choices];
for (const choice of chunk.choices) {
const baseChoice = choices.find((c) => c.index === choice.index);
if (baseChoice) {
baseChoice.finish_reason = choice.finish_reason ?? baseChoice.finish_reason;
baseChoice.message = { ...baseChoice.message, refusal: null };
if (choice.delta?.content)
baseChoice.message.content = (baseChoice.message.content ?? "") + (choice.delta.content ?? "");
if (choice.delta?.function_call) {
const fnCall = baseChoice.message.function_call ?? {
name: "",
arguments: ""
};
fnCall.name = fnCall.name + (choice.delta.function_call.name ?? "");
fnCall.arguments = fnCall.arguments + (choice.delta.function_call.arguments ?? "");
}
if (choice.delta?.tool_calls) {
const toolCalls = baseChoice.message.tool_calls ?? [];
const toolCallDelta = { ...choice.delta.tool_calls[0] };
if (toolCallDelta?.function?.name) {
toolCalls.push({
id: toolCallDelta.id,
type: "function",
function: {
name: toolCallDelta.function.name ?? "",
arguments: toolCallDelta.function.arguments ?? ""
}
});
} else if (toolCalls[toolCalls.length - 1] && toolCallDelta) {
toolCalls[toolCalls.length - 1].function.arguments += toolCallDelta.function?.arguments ?? "";
}
baseChoice.message.tool_calls = toolCalls;
}
} else {
choices.push({ ...omit(choice, "delta"), message: { role: "assistant", ...choice.delta } });
}
}
const merged = {
...base,
choices,
usage: chunk.usage ?? void 0
};
return merged;
}
// src/openai/streaming.ts
var WrappedStream = class extends import_streaming.Stream {
constructor(stream, report) {
super(stream.iterator, stream.controller);
this.resolveReportingFinished = () => {
};
this.report = report;
const reportingFinished = new Promise((resolve2) => {
this.resolveReportingFinished = resolve2;
});
this.openpipe = {
reportingFinished
};
}
async *[Symbol.asyncIterator]() {
const iterator = super[Symbol.asyncIterator]();
let combinedResponse = null;
while (true) {
const result = await iterator.next();
if (result.done) break;
combinedResponse = mergeChunks(combinedResponse, result.value);
yield result.value;
}
await this.report(combinedResponse);
this.resolveReportingFinished();
}
};
// src/codegen/core/BaseHttpRequest.ts
var BaseHttpRequest = class {
constructor(config) {
this.config = config;
}
};
// src/codegen/core/request.ts
var import_form_data = __toESM(require("form-data"), 1);
var import_node_fetch = __toESM(require("node-fetch"), 1);
// src/codegen/core/ApiError.ts
var ApiError = class extends Error {
constructor(request2, response, message) {
super(message);
this.name = "ApiError";
this.url = response.url;
this.status = response.status;
this.statusText = response.statusText;
this.body = response.body;
this.request = request2;
}
};
// src/codegen/core/CancelablePromise.ts
var CancelError = class extends Error {
constructor(message) {
super(message);
this.name = "CancelError";
}
get isCancelled() {
return true;
}
};
var _isResolved, _isRejected, _isCancelled, _cancelHandlers, _promise, _resolve, _reject;
var CancelablePromise = class {
constructor(executor) {
__privateAdd(this, _isResolved);
__privateAdd(this, _isRejected);
__privateAdd(this, _isCancelled);
__privateAdd(this, _cancelHandlers);
__privateAdd(this, _promise);
__privateAdd(this, _resolve);
__privateAdd(this, _reject);
__privateSet(this, _isResolved, false);
__privateSet(this, _isRejected, false);
__privateSet(this, _isCancelled, false);
__privateSet(this, _cancelHandlers, []);
__privateSet(this, _promise, new Promise((resolve2, reject) => {
__privateSet(this, _resolve, resolve2);
__privateSet(this, _reject, reject);
const onResolve = (value) => {
if (__privateGet(this, _isResolved) || __privateGet(this, _isRejected) || __privateGet(this, _isCancelled)) {
return;
}
__privateSet(this, _isResolved, true);
if (__privateGet(this, _resolve)) __privateGet(this, _resolve).call(this, value);
};
const onReject = (reason) => {
if (__privateGet(this, _isResolved) || __privateGet(this, _isRejected) || __privateGet(this, _isCancelled)) {
return;
}
__privateSet(this, _isRejected, true);
if (__privateGet(this, _reject)) __privateGet(this, _reject).call(this, reason);
};
const onCancel = (cancelHandler) => {
if (__privateGet(this, _isResolved) || __privateGet(this, _isRejected) || __privateGet(this, _isCancelled)) {
return;
}
__privateGet(this, _cancelHandlers).push(cancelHandler);
};
Object.defineProperty(onCancel, "isResolved", {
get: () => __privateGet(this, _isResolved)
});
Object.defineProperty(onCancel, "isRejected", {
get: () => __privateGet(this, _isRejected)
});
Object.defineProperty(onCancel, "isCancelled", {
get: () => __privateGet(this, _isCancelled)
});
return executor(onResolve, onReject, onCancel);
}));
}
get [Symbol.toStringTag]() {
return "Cancellable Promise";
}
then(onFulfilled, onRejected) {
return __privateGet(this, _promise).then(onFulfilled, onRejected);
}
catch(onRejected) {
return __privateGet(this, _promise).catch(onRejected);
}
finally(onFinally) {
return __privateGet(this, _promise).finally(onFinally);
}
cancel() {
if (__privateGet(this, _isResolved) || __privateGet(this, _isRejected) || __privateGet(this, _isCancelled)) {
return;
}
__privateSet(this, _isCancelled, true);
if (__privateGet(this, _cancelHandlers).length) {
try {
for (const cancelHandler of __privateGet(this, _cancelHandlers)) {
cancelHandler();
}
} catch (error) {
console.warn("Cancellation threw an error", error);
return;
}
}
__privateGet(this, _cancelHandlers).length = 0;
if (__privateGet(this, _reject)) __privateGet(this, _reject).call(this, new CancelError("Request aborted"));
}
get isCancelled() {
return __privateGet(this, _isCancelled);
}
};
_isResolved = new WeakMap();
_isRejected = new WeakMap();
_isCancelled = new WeakMap();
_cancelHandlers = new WeakMap();
_promise = new WeakMap();
_resolve = new WeakMap();
_reject = new WeakMap();
// src/codegen/core/request.ts
var isDefined = (value) => {
return value !== void 0 && value !== null;
};
var isString = (value) => {
return typeof value === "string";
};
var isStringWithValue = (value) => {
return isString(value) && value !== "";
};
var isBlob = (value) => {
return typeof value === "object" && typeof value.type === "string" && typeof value.stream === "function" && typeof value.arrayBuffer === "function" && typeof value.constructor === "function" && typeof value.constructor.name === "string" && /^(Blob|File)$/.test(value.constructor.name) && /^(Blob|File)$/.test(value[Symbol.toStringTag]);
};
var isFormData = (value) => {
return value instanceof import_form_data.default;
};
var base64 = (str) => {
try {
return btoa(str);
} catch (err) {
return Buffer.from(str).toString("base64");
}
};
var getQueryString = (params) => {
const qs = [];
const append = (key, value) => {
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
};
const process2 = (key, value) => {
if (isDefined(value)) {
if (Array.isArray(value)) {
value.forEach((v) => {
process2(key, v);
});
} else if (typeof value === "object") {
Object.entries(value).forEach(([k, v]) => {
process2(`${key}[${k}]`, v);
});
} else {
append(key, value);
}
}
};
Object.entries(params).forEach(([key, value]) => {
process2(key, value);
});
if (qs.length > 0) {
return `?${qs.join("&")}`;
}
return "";
};
var getUrl = (config, options) => {
const encoder = config.ENCODE_PATH || encodeURI;
const path = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
if (options.path?.hasOwnProperty(group)) {
return encoder(String(options.path[group]));
}
return substring;
});
const url = `${config.BASE}${path}`;
if (options.query) {
return `${url}${getQueryString(options.query)}`;
}
return url;
};
var getFormData = (options) => {
if (options.formData) {
const formData = new import_form_data.default();
const process2 = (key, value) => {
if (isString(value) || isBlob(value)) {
formData.append(key, value);
} else {
formData.append(key, JSON.stringify(value));
}
};
Object.entries(options.formData).filter(([_, value]) => isDefined(value)).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v) => process2(key, v));
} else {
process2(key, value);
}
});
return formData;
}
return void 0;
};
var resolve = async (options, resolver) => {
if (typeof resolver === "function") {
return resolver(options);
}
return resolver;
};
var getHeaders = async (config, options) => {
const token = await resolve(options, config.TOKEN);
const username = await resolve(options, config.USERNAME);
const password = await resolve(options, config.PASSWORD);
const additionalHeaders = await resolve(options, config.HEADERS);
const headers = Object.entries({
Accept: "application/json",
...additionalHeaders,
...options.headers
}).filter(([_, value]) => isDefined(value)).reduce((headers2, [key, value]) => ({
...headers2,
[key]: String(value)
}), {});
if (isStringWithValue(token)) {
headers["Authorization"] = `Bearer ${token}`;
}
if (isStringWithValue(username) && isStringWithValue(password)) {
const credentials = base64(`${username}:${password}`);
headers["Authorization"] = `Basic ${credentials}`;
}
if (options.body) {
if (options.mediaType) {
headers["Content-Type"] = options.mediaType;
} else if (isBlob(options.body)) {
headers["Content-Type"] = "application/octet-stream";
} else if (isString(options.body)) {
headers["Content-Type"] = "text/plain";
} else if (!isFormData(options.body)) {
headers["Content-Type"] = "application/json";
}
}
return new import_node_fetch.Headers(headers);
};
var getRequestBody = (options) => {
if (options.body !== void 0) {
if (options.mediaType?.includes("/json")) {
return JSON.stringify(options.body);
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
return options.body;
} else {
return JSON.stringify(options.body);
}
}
return void 0;
};
var sendRequest = async (options, url, body, formData, headers, onCancel) => {
const controller = new AbortController();
const request2 = {
headers,
method: options.method,
body: body ?? formData,
signal: controller.signal
};
onCancel(() => controller.abort());
return await (0, import_node_fetch.default)(url, request2);
};
var getResponseHeader = (response, responseHeader) => {
if (responseHeader) {
const content = response.headers.get(responseHeader);
if (isString(content)) {
return content;
}
}
return void 0;
};
var getResponseBody = async (response) => {
if (response.status !== 204) {
try {
const contentType = response.headers.get("Content-Type");
if (contentType) {
const jsonTypes = ["application/json", "application/problem+json"];
const isJSON = jsonTypes.some((type) => contentType.toLowerCase().startsWith(type));
if (isJSON) {
return await response.json();
} else {
return await response.text();
}
}
} catch (error) {
console.error(error);
}
}
return void 0;
};
var catchErrorCodes = (options, result) => {
const errors = {
400: "Bad Request",
401: "Unauthorized",
403: "Forbidden",
404: "Not Found",
500: "Internal Server Error",
502: "Bad Gateway",
503: "Service Unavailable",
...options.errors
};
const error = errors[result.status];
if (error) {
throw new ApiError(options, result, error);
}
if (!result.ok) {
const errorStatus = result.status ?? "unknown";
const errorStatusText = result.statusText ?? "unknown";
const errorBody = (() => {
try {
return JSON.stringify(result.body, null, 2);
} catch (e) {
return void 0;
}
})();
throw new ApiError(
options,
result,
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
);
}
};
var request = (config, options) => {
return new CancelablePromise(async (resolve2, reject, onCancel) => {
try {
const url = getUrl(config, options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(config, options);
if (!onCancel.isCancelled) {
const response = await sendRequest(options, url, body, formData, headers, onCancel);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const result = {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: responseHeader ?? responseBody
};
catchErrorCodes(options, result);
resolve2(result.body);
}
} catch (error) {
reject(error);
}
});
};
// src/codegen/core/NodeHttpRequest.ts
var NodeHttpRequest = class extends BaseHttpRequest {
constructor(config) {
super(config);
}
/**
* Request method
* @param options The request options from the service
* @returns CancelablePromise<T>
* @throws ApiError
*/
request(options) {
return request(this.config, options);
}
};
// src/codegen/services/DefaultService.ts
var DefaultService = class {
constructor(httpRequest) {
this.httpRequest = httpRequest;
}
/**
* @deprecated
* DEPRECATED: we no longer support prompt caching.
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
checkCache(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/check-cache",
body: requestBody,
mediaType: "application/json"
});
}
/**
* OpenAI-compatible route for generating inference and optionally logging the request.
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
createChatCompletion(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/chat/completions",
body: requestBody,
mediaType: "application/json"
});
}
/**
* Record request logs from OpenAI models
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
report(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/report",
body: requestBody,
mediaType: "application/json"
});
}
/**
* Record request logs from Anthropic models
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
reportAnthropic(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/report-anthropic",
body: requestBody,
mediaType: "application/json"
});
}
/**
* @deprecated
* DEPRECATED: use "/logs/update-metadata" instead
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
updateLogTags(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/logs/update-tags",
body: requestBody,
mediaType: "application/json"
});
}
/**
* Update tags metadata for logged calls matching the provided filters.
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
updateLogMetadata(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/logs/update-metadata",
body: requestBody,
mediaType: "application/json"
});
}
/**
* Get the latest logged call (only for local testing)
* @returns any Successful response
* @throws ApiError
*/
localTestingOnlyGetLatestLoggedCall() {
return this.httpRequest.request({
method: "GET",
url: "/local-testing-only-get-latest-logged-call"
});
}
/**
* Get a judgement of a completion against the specified criterion
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
getCriterionJudgement(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/criteria/judge",
body: requestBody,
mediaType: "application/json"
});
}
/**
* Create a new dataset.
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
createDataset(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/datasets",
body: requestBody,
mediaType: "application/json"
});
}
/**
* List datasets for a project.
* @returns any Successful response
* @throws ApiError
*/
listDatasets() {
return this.httpRequest.request({
method: "GET",
url: "/datasets"
});
}
/**
* Delete a dataset.
* @param datasetId
* @returns any Successful response
* @throws ApiError
*/
deleteDataset(datasetId) {
return this.httpRequest.request({
method: "DELETE",
url: "/datasets/{datasetId}",
path: {
"datasetId": datasetId
}
});
}
/**
* Add new dataset entries.
* @param datasetId
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
createDatasetEntries(datasetId, requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/datasets/{datasetId}/entries",
path: {
"datasetId": datasetId
},
body: requestBody,
mediaType: "application/json"
});
}
/**
* Train a new model.
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
createModel(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/models",
body: requestBody,
mediaType: "application/json"
});
}
/**
* List all models for a project.
* @returns any Successful response
* @throws ApiError
*/
listModels() {
return this.httpRequest.request({
method: "GET",
url: "/models"
});
}
/**
* Get a model by ID.
* @param modelSlug
* @returns any Successful response
* @throws ApiError
*/
getModel(modelSlug) {
return this.httpRequest.request({
method: "GET",
url: "/models/{modelSlug}",
path: {
"modelSlug": modelSlug
}
});
}
/**
* Delete an existing model.
* @param modelSlug
* @returns any Successful response
* @throws ApiError
*/
deleteModel(modelSlug) {
return this.httpRequest.request({
method: "DELETE",
url: "/models/{modelSlug}",
path: {
"modelSlug": modelSlug
}
});
}
/**
* @deprecated
* DEPRECATED: use the `/datasets` endpoint instead
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
unstableDatasetCreate(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/unstable/dataset/create",
body: requestBody,
mediaType: "application/json"
});
}
/**
* @deprecated
* DEPRECATED: use the `/datasets/{dataset}` endpoint instead
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
unstableDatasetDelete(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/unstable/dataset/delete",
body: requestBody,
mediaType: "application/json"
});
}
/**
* @deprecated
* DEPRECATED: use the `/datasets` endpoint instead
* @returns any Successful response
* @throws ApiError
*/
unstableDatasetList() {
return this.httpRequest.request({
method: "GET",
url: "/unstable/dataset/list"
});
}
/**
* @deprecated
* DEPRECATED: use the `/datasets/{dataset}/entries` endpoint instead
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
unstableDatasetEntryCreate(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/unstable/dataset-entry/create",
body: requestBody,
mediaType: "application/json"
});
}
/**
* @deprecated
* DEPRECATED
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
unstableFinetuneCreate(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/unstable/finetune/create",
body: requestBody,
mediaType: "application/json"
});
}
/**
* @deprecated
* DEPRECATED: use the `/models/{model}` endpoint instead
* @param id
* @param slug
* @returns any Successful response
* @throws ApiError
*/
unstableFinetuneGet(id, slug) {
return this.httpRequest.request({
method: "GET",
url: "/unstable/finetune/get",
query: {
"id": id,
"slug": slug
}
});
}
/**
* @deprecated
* DEPRECATED: use the `/models/{model}` endpoint instead
* @param requestBody
* @returns any Successful response
* @throws ApiError
*/
unstableFinetuneDelete(requestBody) {
return this.httpRequest.request({
method: "POST",
url: "/unstable/finetune/delete",
body: requestBody,
mediaType: "application/json"
});
}
};
// src/codegen/OPClient.ts
var OPClient = class {
constructor(config, HttpRequest = NodeHttpRequest) {
this.request = new HttpRequest({
BASE: config?.BASE ?? "https://api.openpipe.ai/api/v1",
VERSION: config?.VERSION ?? "0.1.1",
WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
CREDENTIALS: config?.CREDENTIALS ?? "include",
TOKEN: config?.TOKEN,
USERNAME: config?.USERNAME,
PASSWORD: config?.PASSWORD,
HEADERS: config?.HEADERS,
ENCODE_PATH: config?.ENCODE_PATH
});
this.default = new DefaultService(this.request);
}
};
// src/shared.ts
var import_error = require("openai/error");
// package.json
var package_default = {
name: "openpipe-dev",
version: "1.0.0",
type: "module",
description: "OpenPipe TypeScript SDK: Fine-Tuning, Inference, and Metrics for Production Apps",
scripts: {
build: "./build.sh",
"build-update": "./build.sh && ./update-app.sh",
test: "vitest --no-file-parallelism",
"test:fast": "vitest --no-file-parallelism -t '^(?!.*\\[slow\\]).*$'"
},
main: "./src/index.ts",
publishConfig: {
name: "openpipe",
access: "public",
main: "./index.cjs",
module: "./index.js",
types: "./index.d.ts",
exports: {
".": {
import: "./index.js",
require: "./index.cjs"
},
"./client": {
import: "./client.js",
require: "./client.cjs"
},
"./openai": {
import: "./openai.js",
require: "./openai.cjs"
},
"./openai/mergeChunks": {
import: "./openai/mergeChunks.js",
require: "./openai/mergeChunks.cjs"
}
}
},
keywords: [
"OpenPipe",
"LLM",
"Fine-tuning",
"AI"
],
author: "",
license: "Apache-2.0",
dependencies: {
"@anthropic-ai/sdk": "^0.39.0",
encoding: "0.1.13",
"form-data": "4.0.1",
"node-fetch": "2.7.0",
openai: "^5.10.2"
},
devDependencies: {
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@types/node": "^22.7.8",
"@types/node-fetch": "^2.6.11",
dotenv: "^16.4.5",
rollup: "^4.24.0",
"rollup-plugin-typescript2": "^0.36.0",
tslib: "^2.8.0",
tsup: "^8.3.0",
tsx: "^4.19.1",
typescript: "^5.6.3",
vitest: "^3.1.3",
yalc: "1.0.0-pre.53",
zod: "^3.23.8"
}
};
// src/shared.ts
var DefaultSdkTags = /* @__PURE__ */ ((DefaultSdkTags2) => {
DefaultSdkTags2["SDK"] = "$sdk";
DefaultSdkTags2["SDK_VERSION"] = "$sdk.version";
return DefaultSdkTags2;
})(DefaultSdkTags || {});
var defaultSdkTags = Object.values(DefaultSdkTags);
var getTags = (args) => ({
...args?.tags,
["$sdk" /* SDK */]: "typescript",
["$sdk.version" /* SDK_VERSION */]: package_default.version
});
// src/helpers.ts
function readEnv(env) {
if (typeof process !== "undefined" && process.env) {
return process.env?.[env]?.trim() ?? void 0;
}
if (typeof window !== "undefined" && env in window) {
return String(window[env]);
}
return void 0;
}
// src/client.ts
var OpenPipe = class {
constructor(config = {}) {
const openPipeApiKey = config?.apiKey ?? readEnv("OPENPIPE_API_KEY");
const openpipeBaseUrl = config?.baseUrl ?? readEnv("OPENPIPE_BASE_URL");
this.baseClient = new OPClient({
BASE: openpipeBaseUrl,
TOKEN: openPipeApiKey
});
}
updateLogTags(...params) {
return this.baseClient.default.updateLogTags(...params);
}
updateLogMetadata(...params) {
return this.baseClient.default.updateLogMetadata(...params);
}
report(...params) {
return this.baseClient.default.report(...params);
}
getCriterionJudgement(...params) {
return this.baseClient.default.getCriterionJudgement(...params);
}
reportAnthropic(...params) {
return this.baseClient.default.reportAnthropic(...params);
}
createDataset(...params) {
return this.baseClient.default.createDataset(...params);
}
unstableDatasetCreate(...params) {
return this.baseClient.default.unstableDatasetCreate(...params);
}
listDatasets(...params) {
return this.baseClient.default.listDatasets(...params);
}
deleteDataset(...params) {
return this.baseClient.default.deleteDataset(...params);
}
unstableDatasetDelete(...params) {
return this.baseClient.default.unstableDatasetDelete(...params);
}
createDatasetEntries(...params) {
return this.baseClient.default.createDatasetEntries(...params);
}
unstableDatasetEntryCreate(...params) {
return this.baseClient.default.unstableDatasetEntryCreate(...params);
}
createModel(...params) {
return this.baseClient.default.createModel(...params);
}
unstableFinetuneCreate(...params) {
return this.baseClient.default.unstableFinetuneCreate(...params);
}
getModel(...params) {
return this.baseClient.default.getModel(...params);
}
unstableFinetuneGet(...params) {
return this.baseClient.default.unstableFinetuneGet(...params);
}
listModels(...params) {
return this.baseClient.default.listModels(...params);
}
deleteModel(...params) {
return this.baseClient.default.deleteModel(...params);
}
unstableFinetuneDelete(...params) {
return this.baseClient.default.unstableFinetuneDelete(...params);
}
};
// src/openai.ts
var import_openai = require("openai");
var import_parser = require("openai/lib/parser");
var import_error2 = require("openai/error");
var MISSING_OPENAI_API_KEY = "MISSING_OPENAI_API_KEY";
var OpenAI2 = class extends openai.OpenAI {
constructor({ openpipe, ...options } = {}) {
super({
...options,
apiKey: options.apiKey || readEnv("OPENAI_API_KEY") || MISSING_OPENAI_API_KEY
});
this.chat = new WrappedChat(this);
const openpipeClient = openpipe instanceof OpenPipe ? openpipe : new OpenPipe(openpipe);
const openPipeApiKey = openpipeClient.baseClient.request.config.TOKEN;
const fallbackClient = openpipe && "fallbackClient" in openpipe && openpipe.fallbackClient instanceof openai.OpenAI ? openpipe.fallbackClient : new openai.OpenAI({ ...options, apiKey: this.apiKey });
if (typeof openPipeApiKey === "string" && openPipeApiKey.length > 0) {
this.chat.setClients(
openpipeClient,
new openai.OpenAI({
...options,
baseURL: openpipeClient.baseClient.request.config.BASE,
apiKey: openPipeApiKey
}),
fallbackClient
);
} else {
console.warn(
"You're using the OpenPipe client without an API key. No completion requests will be logged."
);
}
}
// beta: WrappedBeta = new WrappedBeta(this);
};
var WrappedChat = class extends openai.OpenAI.Chat {
constructor() {
super(...arguments);
this.completions = new WrappedCompletions(this._client);
}
setClients(opClient, opCompletionClient, fallbackClient) {
this.completions.opClient = opClient;
this.completions.opCompletionClient = opCompletionClient;
this.completions.fallbackClient = fallbackClient;
}
};
var WrappedCompletions = class extends openai.OpenAI.Chat.Completions {
constructor(client) {
super(client);
this.openaiClient = client;
this.fallbackClient = client;
}
async _report(args) {
try {
this.opClient ? await this.opClient.report(args) : Promise.resolve();
} catch (e) {
}
}
async _handleResponse(response, usedBody, openpipeArgs, requestedAt) {
let reportingFinished = Promise.resolve();
if (usedBody.stream) {
try {
return new WrappedStream(response, (response2) => {
if (!openpipeArgs.openpipe?.logRequest) return Promise.resolve();
return this._report({
requestedAt,
receivedAt: Date.now(),
reqPayload: usedBody,
respPayload: response2,
statusCode: 200,
tags: getTags(openpipeArgs.openpipe)
});
});
} catch (e) {
console.error("OpenPipe: error creating wrapped stream");
console.error(e);
throw e;
}
} else {
reportingFinished = openpipeArgs.openpipe?.logRequest ? this._report({
requestedAt,
receivedAt: Date.now(),
reqPayload: usedBody,
respPayload: response,
statusCode: 200,
tags: getTags(openpipeArgs.openpipe)
}) : Promise.resolve();
return {
...response,
openpipe: {
reportingFinished
}
};
}
}
async _handleResponseError(error, usedBody, openpipeArgs, requestedAt) {
let reportingFinished = Promise.resolve();
if (error instanceof openai.APIError || error instanceof ApiError) {
const rawMessage = error instanceof openai.APIError ? error.message : error.body.message;
const message = Array.isArray(rawMessage) ? rawMessage.join(", ") : rawMessage;
reportingFinished = this._report({
requestedAt,
receivedAt: Date.now(),
reqPayload: usedBody,
respPayload: error instanceof openai.APIError ? error.error : error.body,
statusCode: error.status,
errorMessage: message,
tags: getTags(openpipeArgs.openpipe)
});
}
if (error !== null) {
error.openpipe = {
reportingFinished
};
}
return error;
}
parse(body, options) {
(0, import_parser.validateInputTools)(body.tools);
const errorMessage = `OpenPipe cannot guarantee json schema for ${body.model}. Use the "chat.completions.create()" API instead.`;
return this._client.chat.completions.create(body, {
...options,
headers: {
...options?.headers,
"X-Stainless-Helper-Method": "chat.completions.parse"
}
}).then((response) => {
if (body.model.startsWith("anthropic:") || body.model.startsWith("gemini:")) {
throw new Error(errorMessage);
}
for (const choice of response.choices) {
if (choice.finish_reason === "length") {
throw new import_error2.LengthFinishReasonError();
}
if (choice.finish_reason === "content_filter") {
throw new import_error2.ContentFilterFinishReasonError();
}
if (choice.message.content) {
try {
JSON.parse(choice.message.content);
} catch (e) {
throw new Error(errorMessage);
}
}
}
try {
return (0, import_parser.parseChatCompletion)(response, body);
} catch (e) {
throw new Error(errorMessage);
}
});
}
async create({ openpipe: rawOpenpipe, ...body }, options) {
const openpipe = { logRequest: true, ...rawOpenpipe };
const requestedAt = Date.now();
if (body.model.startsWith("openpipe:") || body.model.startsWith("openai:") || body.model.startsWith("anthropic:") || body.model.startsWith("gemini:") || openpipe?.cache) {
if (!this.opCompletionClient) throw new Error("OpenPipe client not set");
try {
const response = await this.opCompletionClient.chat.completions.create(body, {
...options,
headers: {
"op-log-request": openpipe.logRequest ? "true" : "false",
"op-cache": openpipe?.cache,
"op-tags": JSON.stringify(getTags(openpipe)),
"op-criteria": JSON.stringify(openpipe.criteria),
...options?.headers
}
});
return response;
} catch (e) {
if (openpipe.fallback?.model) {
const fallbackBody = { ...body, model: openpipe.fallback.model };
try {
const response = await this.fallbackClient.chat.completions.create(fallbackBody, {
...options,
timeout: openpipe.fallback.timeout ?? options?.timeout ?? 10 * 60 * 1e3
});
return this._handleResponse(response, fallbackBody, { openpipe }, requestedAt);
} catch (fallbackError) {
throw await this._handleResponseError(
fallbackError,
fallbackBody,
{ openpipe },
requestedAt
);
}
} else {
throw e;
}
}
}
try {
if (this.openaiClient.apiKey === MISSING_OPENAI_API_KEY) {
throw new import_openai.OpenAIError(
"The OPENAI_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenAI client with an apiKey option, like new OpenAI({ apiKey: 'My API Key' })."
);
}
const openAICompatibleBody = body.metadata && !body.store ? { ...body, metadata: void 0 } : body;
const response = await super.create(openAICompatibleBody, options);
return this._handleResponse(response, body, { openpipe }, requestedAt);
} catch (e) {
throw await this._handleResponseError(e, body, { openpipe }, requestedAt);
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
openai
});
//# sourceMappingURL=index.cjs.map