UNPKG

openpipe

Version:

OpenPipe TypeScript SDK: Fine-Tuning, Inference, and Metrics for Production Apps

1,227 lines (1,209 loc) 36.7 kB
var __typeError = (msg) => { throw TypeError(msg); }; 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/openai.ts import * as openai from "openai"; // src/openai/streaming.ts import { Stream } from "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 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 import FormData from "form-data"; import fetch, { Headers } from "node-fetch"; // 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 FormData; }; 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 FormData(); 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 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 fetch(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 import { APIConnectionTimeoutError } from "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 import { OpenAIError } from "openai"; import { validateInputTools, parseChatCompletion } from "openai/lib/parser"; import { ContentFilterFinishReasonError, LengthFinishReasonError } from "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) { 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 LengthFinishReasonError(); } if (choice.finish_reason === "content_filter") { throw new ContentFilterFinishReasonError(); } if (choice.message.content) { try { JSON.parse(choice.message.content); } catch (e) { throw new Error(errorMessage); } } } try { return 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 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); } } }; export { OpenAI2 as default }; //# sourceMappingURL=openai.js.map