openpipe
Version:
OpenPipe TypeScript SDK: Fine-Tuning, Inference, and Metrics for Production Apps
1 lines • 115 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/openai.ts","../src/openai/streaming.ts","../src/openai/mergeChunks.ts","../src/codegen/core/BaseHttpRequest.ts","../src/codegen/core/request.ts","../src/codegen/core/ApiError.ts","../src/codegen/core/CancelablePromise.ts","../src/codegen/core/NodeHttpRequest.ts","../src/codegen/services/DefaultService.ts","../src/codegen/OPClient.ts","../src/shared.ts","../package.json","../src/helpers.ts","../src/client.ts"],"sourcesContent":["export * as openai from \"./openai\";\nexport type { OpenPipeMeta, OpenPipeArgs, OpenPipeConfig, OpenPipeChatCompletion } from \"./shared\";\n","import * as openai from \"openai\";\n\nimport type {\n ChatCompletion,\n ChatCompletionChunk,\n ChatCompletionCreateParams,\n ChatCompletionCreateParamsBase,\n ChatCompletionCreateParamsNonStreaming,\n ChatCompletionCreateParamsStreaming,\n ChatCompletionParseParams,\n ParsedChatCompletion,\n} from \"openai/resources/chat/completions\";\n\nimport { WrappedStream } from \"./openai/streaming\";\nimport { ApiError, DefaultService } from \"./codegen\";\nimport type { Stream } from \"openai/streaming\";\nimport {\n OpenPipeArgs,\n OpenPipeMeta,\n type OpenPipeConfig,\n getTags,\n OpenPipeChatCompletion,\n} from \"./shared\";\nimport OpenPipe from \"./client\";\nimport { OpenAIError } from \"openai\";\nimport { readEnv } from \"./helpers\";\nimport { RequestOptions } from \"openai/internal/request-options\";\nimport {\n ExtractParsedContentFromParams,\n validateInputTools,\n parseChatCompletion,\n} from \"openai/lib/parser\";\nimport { ContentFilterFinishReasonError, LengthFinishReasonError } from \"openai/error\";\n\nconst MISSING_OPENAI_API_KEY = \"MISSING_OPENAI_API_KEY\";\n\nexport type ClientOptions = openai.ClientOptions & { openpipe?: OpenPipeConfig | OpenPipe };\n\nexport default class OpenAI extends openai.OpenAI {\n constructor({ openpipe, ...options }: ClientOptions = {}) {\n super({\n ...options,\n apiKey: options.apiKey || readEnv(\"OPENAI_API_KEY\") || MISSING_OPENAI_API_KEY,\n });\n const openpipeClient = openpipe instanceof OpenPipe ? openpipe : new OpenPipe(openpipe);\n const openPipeApiKey = openpipeClient.baseClient.request.config.TOKEN;\n const fallbackClient =\n openpipe && \"fallbackClient\" in openpipe && openpipe.fallbackClient instanceof openai.OpenAI\n ? openpipe.fallbackClient\n : new openai.OpenAI({ ...options, apiKey: this.apiKey });\n\n if (typeof openPipeApiKey === \"string\" && openPipeApiKey.length > 0) {\n this.chat.setClients(\n openpipeClient,\n new openai.OpenAI({\n ...options,\n baseURL: openpipeClient.baseClient.request.config.BASE,\n apiKey: openPipeApiKey,\n }),\n fallbackClient,\n );\n } else {\n console.warn(\n \"You're using the OpenPipe client without an API key. No completion requests will be logged.\",\n );\n }\n }\n chat: WrappedChat = new WrappedChat(this);\n // beta: WrappedBeta = new WrappedBeta(this);\n}\n\nclass WrappedChat extends openai.OpenAI.Chat {\n completions: WrappedCompletions = new WrappedCompletions(this._client);\n\n setClients(opClient: OpenPipe, opCompletionClient: openai.OpenAI, fallbackClient: openai.OpenAI) {\n this.completions.opClient = opClient;\n this.completions.opCompletionClient = opCompletionClient;\n this.completions.fallbackClient = fallbackClient;\n }\n}\n\nclass WrappedCompletions extends openai.OpenAI.Chat.Completions {\n // keep a reference to the original client so we can read options from it\n openaiClient: openai.OpenAI;\n opClient?: OpenPipe;\n opCompletionClient?: openai.OpenAI;\n fallbackClient: openai.OpenAI;\n\n constructor(client: openai.OpenAI) {\n super(client);\n this.openaiClient = client;\n this.fallbackClient = client;\n }\n\n async _report(args: Parameters<DefaultService[\"report\"]>[0]) {\n try {\n this.opClient ? await this.opClient.report(args) : Promise.resolve();\n } catch (e) {\n // Ignore errors with reporting\n }\n }\n\n async _handleResponse(\n response: ChatCompletion | Stream<ChatCompletionChunk>,\n usedBody: ChatCompletionCreateParams,\n openpipeArgs: OpenPipeArgs,\n requestedAt: number,\n ) {\n let reportingFinished: OpenPipeMeta[\"reportingFinished\"] = Promise.resolve();\n if (usedBody.stream) {\n try {\n return new WrappedStream(response as Stream<ChatCompletionChunk>, (response) => {\n if (!openpipeArgs.openpipe?.logRequest) return Promise.resolve();\n return this._report({\n requestedAt,\n receivedAt: Date.now(),\n reqPayload: usedBody,\n respPayload: response,\n statusCode: 200,\n tags: getTags(openpipeArgs.openpipe),\n });\n });\n } catch (e) {\n console.error(\"OpenPipe: error creating wrapped stream\");\n console.error(e);\n throw e;\n }\n } else {\n reportingFinished = openpipeArgs.openpipe?.logRequest\n ? this._report({\n requestedAt,\n receivedAt: Date.now(),\n reqPayload: usedBody,\n respPayload: response,\n statusCode: 200,\n tags: getTags(openpipeArgs.openpipe),\n })\n : Promise.resolve();\n return {\n ...(response as ChatCompletion),\n openpipe: {\n reportingFinished,\n },\n };\n }\n }\n\n async _handleResponseError(\n error: unknown,\n usedBody: ChatCompletionCreateParams,\n openpipeArgs: OpenPipeArgs,\n requestedAt: number,\n ) {\n let reportingFinished: OpenPipeMeta[\"reportingFinished\"] = Promise.resolve();\n\n if (error instanceof openai.APIError || error instanceof ApiError) {\n const rawMessage =\n error instanceof openai.APIError\n ? (error.message as string | string[])\n : error.body.message;\n const message = Array.isArray(rawMessage) ? rawMessage.join(\", \") : rawMessage;\n\n reportingFinished = this._report({\n requestedAt,\n receivedAt: Date.now(),\n reqPayload: usedBody,\n respPayload: error instanceof openai.APIError ? error.error : error.body,\n statusCode: error.status,\n errorMessage: message,\n tags: getTags(openpipeArgs.openpipe),\n });\n }\n\n if (error !== null) {\n (error as { openpipe?: any }).openpipe = {\n reportingFinished,\n };\n }\n\n return error;\n }\n\n parse<Params extends ChatCompletionParseParams, ParsedT = ExtractParsedContentFromParams<Params>>(\n body: Params,\n options?: RequestOptions,\n ): openai.APIPromise<ParsedChatCompletion<ParsedT> & { openpipe?: OpenPipeMeta }> {\n validateInputTools(body.tools);\n const errorMessage = `OpenPipe cannot guarantee json schema for ${body.model}. Use the \"chat.completions.create()\" API instead.`;\n\n return this._client.chat.completions\n .create(body, {\n ...options,\n headers: {\n ...options?.headers,\n \"X-Stainless-Helper-Method\": \"chat.completions.parse\",\n },\n })\n .then((response) => {\n // We can only guarantee json schema for OpenAI models. But we do not know if we are calling an OpenAI ft model, and any other model.\n // Therefore, we either disable this feature for all openpipe models, including OpenAI models, or we throw errors during the runtime.\n if (body.model.startsWith(\"anthropic:\") || body.model.startsWith(\"gemini:\")) {\n throw new Error(errorMessage);\n }\n for (const choice of response.choices) {\n // If the model stops generating tokens due to length or content filter, we throw the same errors as parseChatCompletion\n if (choice.finish_reason === \"length\") {\n throw new LengthFinishReasonError();\n }\n if (choice.finish_reason === \"content_filter\") {\n throw new ContentFilterFinishReasonError();\n }\n\n if (choice.message.content) {\n try {\n JSON.parse(choice.message.content);\n } catch (e) {\n throw new Error(errorMessage);\n }\n }\n }\n try {\n return parseChatCompletion(response, body);\n } catch (e) {\n throw new Error(errorMessage);\n }\n }) as openai.APIPromise<ParsedChatCompletion<ParsedT> & { openpipe?: OpenPipeMeta }>;\n }\n // @ts-expect-error It doesn't like the fact that I added a `Promise<>`\n // wrapper but I actually think the types are correct here.\n create(\n body: ChatCompletionCreateParamsNonStreaming & OpenPipeArgs,\n options?: RequestOptions,\n ): openai.APIPromise<OpenPipeChatCompletion & { openpipe?: OpenPipeMeta }>;\n create(\n body: ChatCompletionCreateParamsStreaming & OpenPipeArgs,\n options?: RequestOptions,\n ): openai.APIPromise<Stream<ChatCompletionChunk> & { openpipe?: OpenPipeMeta }>;\n create(\n body: ChatCompletionCreateParamsBase & OpenPipeArgs,\n options?: RequestOptions,\n ): openai.APIPromise<Stream<ChatCompletionChunk> | OpenPipeChatCompletion>;\n async create(\n { openpipe: rawOpenpipe, ...body }: ChatCompletionCreateParams & OpenPipeArgs,\n options?: RequestOptions,\n ): Promise<\n openai.APIPromise<\n | (OpenPipeChatCompletion & { openpipe?: OpenPipeMeta })\n | (Stream<ChatCompletionChunk> & { openpipe?: OpenPipeMeta })\n >\n > {\n const openpipe = { logRequest: true, ...rawOpenpipe };\n const requestedAt = Date.now();\n\n if (\n body.model.startsWith(\"openpipe:\") ||\n body.model.startsWith(\"openai:\") ||\n body.model.startsWith(\"anthropic:\") ||\n body.model.startsWith(\"gemini:\") ||\n openpipe?.cache\n ) {\n if (!this.opCompletionClient) throw new Error(\"OpenPipe client not set\");\n try {\n const response = await this.opCompletionClient.chat.completions.create(body, {\n ...options,\n headers: {\n \"op-log-request\": openpipe.logRequest ? \"true\" : \"false\",\n \"op-cache\": openpipe?.cache,\n \"op-tags\": JSON.stringify(getTags(openpipe)),\n \"op-criteria\": JSON.stringify(openpipe.criteria),\n ...options?.headers,\n },\n });\n return response;\n } catch (e) {\n if (openpipe.fallback?.model) {\n const fallbackBody = { ...body, model: openpipe.fallback.model };\n try {\n const response = await this.fallbackClient.chat.completions.create(fallbackBody, {\n ...options,\n timeout: openpipe.fallback.timeout ?? options?.timeout ?? 10 * 60 * 1000,\n });\n\n return this._handleResponse(response, fallbackBody, { openpipe }, requestedAt);\n } catch (fallbackError) {\n throw await this._handleResponseError(\n fallbackError,\n fallbackBody,\n { openpipe },\n requestedAt,\n );\n }\n } else {\n throw e;\n }\n }\n }\n\n try {\n if (this.openaiClient.apiKey === MISSING_OPENAI_API_KEY) {\n throw new OpenAIError(\n \"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' }).\",\n );\n }\n\n // OpenAI does not accept metadata if store is false\n const openAICompatibleBody =\n body.metadata && !body.store ? { ...body, metadata: undefined } : body;\n\n const response = await super.create(openAICompatibleBody, options);\n return this._handleResponse(response, body, { openpipe }, requestedAt);\n } catch (e: unknown) {\n throw await this._handleResponseError(e, body, { openpipe }, requestedAt);\n }\n }\n}\n","import type { ChatCompletion, ChatCompletionChunk } from \"openai/resources/chat\";\nimport { Stream } from \"openai/streaming\";\n\nimport { OpenPipeMeta } from \"../shared\";\nimport mergeChunks from \"./mergeChunks\";\n\nexport class WrappedStream extends Stream<ChatCompletionChunk> {\n openpipe: OpenPipeMeta;\n\n private resolveReportingFinished: () => void = () => {};\n private report: (response: unknown) => Promise<void>;\n\n constructor(stream: Stream<ChatCompletionChunk>, report: (response: unknown) => Promise<void>) {\n // @ts-expect-error - This is a private property but we need to access it\n super(stream.iterator, stream.controller);\n this.report = report;\n\n const reportingFinished = new Promise<void>((resolve) => {\n this.resolveReportingFinished = resolve;\n });\n\n this.openpipe = {\n reportingFinished,\n };\n }\n\n async *[Symbol.asyncIterator](): AsyncIterator<ChatCompletionChunk, any, undefined> {\n const iterator = super[Symbol.asyncIterator]();\n\n let combinedResponse: ChatCompletion | null = null;\n while (true) {\n const result = await iterator.next();\n if (result.done) break;\n combinedResponse = mergeChunks(combinedResponse, result.value);\n\n yield result.value;\n }\n\n await this.report(combinedResponse);\n\n // Resolve the promise here\n this.resolveReportingFinished();\n }\n}\n","import type { ChatCompletion, ChatCompletionChunk } from \"openai/resources/chat/completions\";\n\nconst omit = <T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n ...keys: K[]\n): Omit<T, K> => {\n const ret = { ...obj };\n for (const key of keys) {\n delete ret[key];\n }\n return ret;\n};\n\nexport default function mergeChunks(\n base: ChatCompletion | null,\n chunk: ChatCompletionChunk,\n): ChatCompletion {\n if (base === null) {\n return mergeChunks(\n { ...chunk, object: \"chat.completion\", choices: [], usage: chunk.usage ?? undefined },\n // Prevent function call and tool call arguments from being double-merged\n {\n ...chunk,\n choices: chunk.choices.map((c) => ({\n ...c,\n delta: {\n ...c.delta,\n function_call: c.delta.function_call\n ? {\n ...c.delta.function_call,\n }\n : undefined,\n tool_calls: c.delta.tool_calls?.map((tc) => ({\n ...tc,\n function: {\n ...tc.function,\n },\n })),\n },\n })),\n },\n );\n }\n\n const choices = [...base.choices];\n for (const choice of chunk.choices) {\n const baseChoice = choices.find((c) => c.index === choice.index);\n if (baseChoice) {\n baseChoice.finish_reason = choice.finish_reason ?? baseChoice.finish_reason;\n baseChoice.message = { ...baseChoice.message, refusal: null };\n\n if (choice.delta?.content)\n baseChoice.message.content =\n (baseChoice.message.content ?? \"\") + (choice.delta.content ?? \"\");\n if (choice.delta?.function_call) {\n const fnCall = baseChoice.message.function_call ?? {\n name: \"\",\n arguments: \"\",\n };\n fnCall.name = fnCall.name + (choice.delta.function_call.name ?? \"\");\n fnCall.arguments = fnCall.arguments + (choice.delta.function_call.arguments ?? \"\");\n }\n if (choice.delta?.tool_calls) {\n const toolCalls = baseChoice.message.tool_calls ?? [];\n const toolCallDelta = { ...choice.delta.tool_calls[0] };\n if (toolCallDelta?.function?.name) {\n toolCalls.push({\n id: toolCallDelta.id as string,\n type: \"function\",\n function: {\n name: toolCallDelta.function.name ?? \"\",\n arguments: toolCallDelta.function.arguments ?? \"\",\n },\n });\n } else if (toolCalls[toolCalls.length - 1] && toolCallDelta) {\n toolCalls[toolCalls.length - 1]!.function.arguments +=\n toolCallDelta.function?.arguments ?? \"\";\n }\n baseChoice.message.tool_calls = toolCalls;\n }\n } else {\n // @ts-expect-error the types are correctly telling us that finish_reason\n // could be null, but don't want to fix it right now.\n choices.push({ ...omit(choice, \"delta\"), message: { role: \"assistant\", ...choice.delta } });\n }\n }\n\n const merged: ChatCompletion = {\n ...base,\n choices,\n usage: chunk.usage ?? undefined,\n };\n\n return merged;\n}\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nimport type { ApiRequestOptions } from './ApiRequestOptions';\nimport type { CancelablePromise } from './CancelablePromise';\nimport type { OpenAPIConfig } from './OpenAPI';\n\nexport abstract class BaseHttpRequest {\n\n constructor(public readonly config: OpenAPIConfig) {}\n\n public abstract request<T>(options: ApiRequestOptions): CancelablePromise<T>;\n}\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nimport FormData from 'form-data';\nimport fetch, { Headers } from 'node-fetch';\nimport type { RequestInit, Response } from 'node-fetch';\nimport type { AbortSignal } from 'node-fetch/externals';\n\nimport { ApiError } from './ApiError';\nimport type { ApiRequestOptions } from './ApiRequestOptions';\nimport type { ApiResult } from './ApiResult';\nimport { CancelablePromise } from './CancelablePromise';\nimport type { OnCancel } from './CancelablePromise';\nimport type { OpenAPIConfig } from './OpenAPI';\n\nexport const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {\n return value !== undefined && value !== null;\n};\n\nexport const isString = (value: any): value is string => {\n return typeof value === 'string';\n};\n\nexport const isStringWithValue = (value: any): value is string => {\n return isString(value) && value !== '';\n};\n\nexport const isBlob = (value: any): value is Blob => {\n return (\n typeof value === 'object' &&\n typeof value.type === 'string' &&\n typeof value.stream === 'function' &&\n typeof value.arrayBuffer === 'function' &&\n typeof value.constructor === 'function' &&\n typeof value.constructor.name === 'string' &&\n /^(Blob|File)$/.test(value.constructor.name) &&\n /^(Blob|File)$/.test(value[Symbol.toStringTag])\n );\n};\n\nexport const isFormData = (value: any): value is FormData => {\n return value instanceof FormData;\n};\n\nexport const base64 = (str: string): string => {\n try {\n return btoa(str);\n } catch (err) {\n // @ts-ignore\n return Buffer.from(str).toString('base64');\n }\n};\n\nexport const getQueryString = (params: Record<string, any>): string => {\n const qs: string[] = [];\n\n const append = (key: string, value: any) => {\n qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);\n };\n\n const process = (key: string, value: any) => {\n if (isDefined(value)) {\n if (Array.isArray(value)) {\n value.forEach(v => {\n process(key, v);\n });\n } else if (typeof value === 'object') {\n Object.entries(value).forEach(([k, v]) => {\n process(`${key}[${k}]`, v);\n });\n } else {\n append(key, value);\n }\n }\n };\n\n Object.entries(params).forEach(([key, value]) => {\n process(key, value);\n });\n\n if (qs.length > 0) {\n return `?${qs.join('&')}`;\n }\n\n return '';\n};\n\nconst getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {\n const encoder = config.ENCODE_PATH || encodeURI;\n\n const path = options.url\n .replace('{api-version}', config.VERSION)\n .replace(/{(.*?)}/g, (substring: string, group: string) => {\n if (options.path?.hasOwnProperty(group)) {\n return encoder(String(options.path[group]));\n }\n return substring;\n });\n\n const url = `${config.BASE}${path}`;\n if (options.query) {\n return `${url}${getQueryString(options.query)}`;\n }\n return url;\n};\n\nexport const getFormData = (options: ApiRequestOptions): FormData | undefined => {\n if (options.formData) {\n const formData = new FormData();\n\n const process = (key: string, value: any) => {\n if (isString(value) || isBlob(value)) {\n formData.append(key, value);\n } else {\n formData.append(key, JSON.stringify(value));\n }\n };\n\n Object.entries(options.formData)\n .filter(([_, value]) => isDefined(value))\n .forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach(v => process(key, v));\n } else {\n process(key, value);\n }\n });\n\n return formData;\n }\n return undefined;\n};\n\ntype Resolver<T> = (options: ApiRequestOptions) => Promise<T>;\n\nexport const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {\n if (typeof resolver === 'function') {\n return (resolver as Resolver<T>)(options);\n }\n return resolver;\n};\n\nexport const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise<Headers> => {\n const token = await resolve(options, config.TOKEN);\n const username = await resolve(options, config.USERNAME);\n const password = await resolve(options, config.PASSWORD);\n const additionalHeaders = await resolve(options, config.HEADERS);\n\n const headers = Object.entries({\n Accept: 'application/json',\n ...additionalHeaders,\n ...options.headers,\n })\n .filter(([_, value]) => isDefined(value))\n .reduce((headers, [key, value]) => ({\n ...headers,\n [key]: String(value),\n }), {} as Record<string, string>);\n\n if (isStringWithValue(token)) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n\n if (isStringWithValue(username) && isStringWithValue(password)) {\n const credentials = base64(`${username}:${password}`);\n headers['Authorization'] = `Basic ${credentials}`;\n }\n\n if (options.body) {\n if (options.mediaType) {\n headers['Content-Type'] = options.mediaType;\n } else if (isBlob(options.body)) {\n headers['Content-Type'] = 'application/octet-stream';\n } else if (isString(options.body)) {\n headers['Content-Type'] = 'text/plain';\n } else if (!isFormData(options.body)) {\n headers['Content-Type'] = 'application/json';\n }\n }\n\n return new Headers(headers);\n};\n\nexport const getRequestBody = (options: ApiRequestOptions): any => {\n if (options.body !== undefined) {\n if (options.mediaType?.includes('/json')) {\n return JSON.stringify(options.body)\n } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {\n return options.body as any;\n } else {\n return JSON.stringify(options.body);\n }\n }\n return undefined;\n};\n\nexport const sendRequest = async (\n options: ApiRequestOptions,\n url: string,\n body: any,\n formData: FormData | undefined,\n headers: Headers,\n onCancel: OnCancel\n): Promise<Response> => {\n const controller = new AbortController();\n\n const request: RequestInit = {\n headers,\n method: options.method,\n body: body ?? formData,\n signal: controller.signal as AbortSignal,\n };\n\n onCancel(() => controller.abort());\n\n return await fetch(url, request);\n};\n\nexport const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {\n if (responseHeader) {\n const content = response.headers.get(responseHeader);\n if (isString(content)) {\n return content;\n }\n }\n return undefined;\n};\n\nexport const getResponseBody = async (response: Response): Promise<any> => {\n if (response.status !== 204) {\n try {\n const contentType = response.headers.get('Content-Type');\n if (contentType) {\n const jsonTypes = ['application/json', 'application/problem+json']\n const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));\n if (isJSON) {\n return await response.json();\n } else {\n return await response.text();\n }\n }\n } catch (error) {\n console.error(error);\n }\n }\n return undefined;\n};\n\nexport const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {\n const errors: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 500: 'Internal Server Error',\n 502: 'Bad Gateway',\n 503: 'Service Unavailable',\n ...options.errors,\n }\n\n const error = errors[result.status];\n if (error) {\n throw new ApiError(options, result, error);\n }\n\n if (!result.ok) {\n const errorStatus = result.status ?? 'unknown';\n const errorStatusText = result.statusText ?? 'unknown';\n const errorBody = (() => {\n try {\n return JSON.stringify(result.body, null, 2);\n } catch (e) {\n return undefined;\n }\n })();\n\n throw new ApiError(options, result,\n `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`\n );\n }\n};\n\n/**\n * Request method\n * @param config The OpenAPI configuration object\n * @param options The request options from the service\n * @returns CancelablePromise<T>\n * @throws ApiError\n */\nexport const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {\n return new CancelablePromise(async (resolve, reject, onCancel) => {\n try {\n const url = getUrl(config, options);\n const formData = getFormData(options);\n const body = getRequestBody(options);\n const headers = await getHeaders(config, options);\n\n if (!onCancel.isCancelled) {\n const response = await sendRequest(options, url, body, formData, headers, onCancel);\n const responseBody = await getResponseBody(response);\n const responseHeader = getResponseHeader(response, options.responseHeader);\n\n const result: ApiResult = {\n url,\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n body: responseHeader ?? responseBody,\n };\n\n catchErrorCodes(options, result);\n\n resolve(result.body);\n }\n } catch (error) {\n reject(error);\n }\n });\n};\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nimport type { ApiRequestOptions } from './ApiRequestOptions';\nimport type { ApiResult } from './ApiResult';\n\nexport class ApiError extends Error {\n public readonly url: string;\n public readonly status: number;\n public readonly statusText: string;\n public readonly body: any;\n public readonly request: ApiRequestOptions;\n\n constructor(request: ApiRequestOptions, response: ApiResult, message: string) {\n super(message);\n\n this.name = 'ApiError';\n this.url = response.url;\n this.status = response.status;\n this.statusText = response.statusText;\n this.body = response.body;\n this.request = request;\n }\n}\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nexport class CancelError extends Error {\n\n constructor(message: string) {\n super(message);\n this.name = 'CancelError';\n }\n\n public get isCancelled(): boolean {\n return true;\n }\n}\n\nexport interface OnCancel {\n readonly isResolved: boolean;\n readonly isRejected: boolean;\n readonly isCancelled: boolean;\n\n (cancelHandler: () => void): void;\n}\n\nexport class CancelablePromise<T> implements Promise<T> {\n #isResolved: boolean;\n #isRejected: boolean;\n #isCancelled: boolean;\n readonly #cancelHandlers: (() => void)[];\n readonly #promise: Promise<T>;\n #resolve?: (value: T | PromiseLike<T>) => void;\n #reject?: (reason?: any) => void;\n\n constructor(\n executor: (\n resolve: (value: T | PromiseLike<T>) => void,\n reject: (reason?: any) => void,\n onCancel: OnCancel\n ) => void\n ) {\n this.#isResolved = false;\n this.#isRejected = false;\n this.#isCancelled = false;\n this.#cancelHandlers = [];\n this.#promise = new Promise<T>((resolve, reject) => {\n this.#resolve = resolve;\n this.#reject = reject;\n\n const onResolve = (value: T | PromiseLike<T>): void => {\n if (this.#isResolved || this.#isRejected || this.#isCancelled) {\n return;\n }\n this.#isResolved = true;\n if (this.#resolve) this.#resolve(value);\n };\n\n const onReject = (reason?: any): void => {\n if (this.#isResolved || this.#isRejected || this.#isCancelled) {\n return;\n }\n this.#isRejected = true;\n if (this.#reject) this.#reject(reason);\n };\n\n const onCancel = (cancelHandler: () => void): void => {\n if (this.#isResolved || this.#isRejected || this.#isCancelled) {\n return;\n }\n this.#cancelHandlers.push(cancelHandler);\n };\n\n Object.defineProperty(onCancel, 'isResolved', {\n get: (): boolean => this.#isResolved,\n });\n\n Object.defineProperty(onCancel, 'isRejected', {\n get: (): boolean => this.#isRejected,\n });\n\n Object.defineProperty(onCancel, 'isCancelled', {\n get: (): boolean => this.#isCancelled,\n });\n\n return executor(onResolve, onReject, onCancel as OnCancel);\n });\n }\n\n get [Symbol.toStringTag]() {\n return \"Cancellable Promise\";\n }\n\n public then<TResult1 = T, TResult2 = never>(\n onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,\n onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null\n ): Promise<TResult1 | TResult2> {\n return this.#promise.then(onFulfilled, onRejected);\n }\n\n public catch<TResult = never>(\n onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null\n ): Promise<T | TResult> {\n return this.#promise.catch(onRejected);\n }\n\n public finally(onFinally?: (() => void) | null): Promise<T> {\n return this.#promise.finally(onFinally);\n }\n\n public cancel(): void {\n if (this.#isResolved || this.#isRejected || this.#isCancelled) {\n return;\n }\n this.#isCancelled = true;\n if (this.#cancelHandlers.length) {\n try {\n for (const cancelHandler of this.#cancelHandlers) {\n cancelHandler();\n }\n } catch (error) {\n console.warn('Cancellation threw an error', error);\n return;\n }\n }\n this.#cancelHandlers.length = 0;\n if (this.#reject) this.#reject(new CancelError('Request aborted'));\n }\n\n public get isCancelled(): boolean {\n return this.#isCancelled;\n }\n}\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nimport type { ApiRequestOptions } from './ApiRequestOptions';\nimport { BaseHttpRequest } from './BaseHttpRequest';\nimport type { CancelablePromise } from './CancelablePromise';\nimport type { OpenAPIConfig } from './OpenAPI';\nimport { request as __request } from './request';\n\nexport class NodeHttpRequest extends BaseHttpRequest {\n\n constructor(config: OpenAPIConfig) {\n super(config);\n }\n\n /**\n * Request method\n * @param options The request options from the service\n * @returns CancelablePromise<T>\n * @throws ApiError\n */\n public override request<T>(options: ApiRequestOptions): CancelablePromise<T> {\n return __request(this.config, options);\n }\n}\n","/* generated using openapi-typescript-codegen -- do no edit */\n/* istanbul ignore file */\n/* tslint:disable */\n/* eslint-disable */\nimport type { CancelablePromise } from '../core/CancelablePromise';\nimport type { BaseHttpRequest } from '../core/BaseHttpRequest';\nexport class DefaultService {\n constructor(public readonly httpRequest: BaseHttpRequest) {}\n /**\n * @deprecated\n * DEPRECATED: we no longer support prompt caching.\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public checkCache(\n requestBody: {\n /**\n * Unix timestamp in milliseconds\n */\n requestedAt: number;\n /**\n * JSON-encoded request payload\n */\n reqPayload?: any;\n /**\n * Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"prompt_id\": \"populate-title\" }\n */\n tags?: Record<string, string>;\n },\n ): CancelablePromise<{\n /**\n * JSON-encoded response payload\n */\n respPayload?: any;\n }> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/check-cache',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * OpenAI-compatible route for generating inference and optionally logging the request.\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public createChatCompletion(\n requestBody: Record<string, any>,\n ): CancelablePromise<{\n id: string;\n object: 'chat.completion';\n created: number;\n model: string;\n choices: Array<{\n finish_reason: ('length' | 'function_call' | 'tool_calls' | 'stop' | 'content_filter');\n index: number;\n message: {\n reasoning_content?: string | null;\n content?: string | null;\n refusal?: string | null;\n role: 'assistant';\n function_call?: {\n name?: string;\n arguments?: string;\n } | null;\n tool_calls?: Array<{\n id: string;\n function: {\n name: string;\n arguments: string;\n };\n type: 'function';\n }> | null;\n };\n logprobs?: {\n content?: Array<{\n token: string;\n bytes: Array<number> | null;\n logprob: number;\n top_logprobs: Array<{\n token: string;\n bytes: Array<number> | null;\n logprob: number;\n }>;\n }> | null;\n refusal?: Array<{\n token: string;\n bytes: Array<number> | null;\n logprob: number;\n top_logprobs: Array<{\n token: string;\n bytes: Array<number> | null;\n logprob: number;\n }>;\n }> | null;\n } | null;\n content_filter_results?: Record<string, any>;\n criteria_results?: Record<string, ({\n status: 'success';\n score: number;\n explanation?: string;\n errorCode?: number;\n errorMessage?: string;\n } | {\n status: 'error';\n score?: number;\n explanation?: string;\n errorCode: number;\n errorMessage: string;\n })>;\n }>;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n prompt_cache_hit_tokens?: number;\n prompt_cache_miss_tokens?: number;\n completion_tokens_details?: {\n reasoning_tokens?: number | null;\n audio_tokens?: number | null;\n text_tokens?: number | null;\n accepted_prediction_tokens?: number | null;\n rejected_prediction_tokens?: number | null;\n } | null;\n prompt_tokens_details?: {\n cached_tokens?: number | null;\n audio_tokens?: number | null;\n } | null;\n criteria?: Record<string, {\n /**\n * The total number of tokens used to generate the criterion judgement. Only returned for OpenPipe-trained reward models currently.\n */\n total_tokens: number;\n }>;\n };\n } | null> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/chat/completions',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * Record request logs from OpenAI models\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public report(\n requestBody: {\n /**\n * Unix timestamp in milliseconds\n */\n requestedAt?: number;\n /**\n * Unix timestamp in milliseconds\n */\n receivedAt?: number;\n /**\n * JSON-encoded request payload\n */\n reqPayload?: any;\n /**\n * JSON-encoded response payload\n */\n respPayload?: any;\n /**\n * HTTP status code of response\n */\n statusCode?: number;\n /**\n * User-friendly error message\n */\n errorMessage?: string;\n /**\n * DEPRECATED: use \"reqPayload.metadata\" to attach extra metadata tags to the call for filtering. Eg { \"userId\": \"123\", \"prompt_id\": \"populate-title\" }\n */\n tags?: Record<string, (string | number | boolean | 'null' | null)>;\n },\n ): CancelablePromise<{\n status: ('ok' | 'error');\n }> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/report',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * Record request logs from Anthropic models\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public reportAnthropic(\n requestBody: {\n /**\n * Unix timestamp in milliseconds\n */\n requestedAt?: number;\n /**\n * Unix timestamp in milliseconds\n */\n receivedAt?: number;\n /**\n * JSON-encoded request payload\n */\n reqPayload?: Record<string, any>;\n /**\n * JSON-encoded response payload\n */\n respPayload?: {\n id: string;\n content: Array<({\n text: string;\n type: 'text';\n citations?: Array<({\n cited_text: string;\n document_index: number;\n document_title: string | null;\n end_char_index: number;\n start_char_index: number;\n type: 'char_location';\n } | {\n cited_text: string;\n document_index: number;\n document_title: string | null;\n end_page_number: number;\n start_page_number: number;\n type: 'page_location';\n } | {\n cited_text: string;\n document_index: number;\n document_title: string | null;\n end_block_index: number;\n start_block_index: number;\n type: 'content_block_location';\n })> | null;\n } | {\n id: string;\n name: string;\n type: 'tool_use';\n input?: any;\n } | {\n thinking: string;\n signature: string;\n type: 'thinking';\n } | {\n data: string;\n type: 'redacted_thinking';\n })>;\n model: string;\n role: 'assistant';\n stop_reason: ('end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use' | 'null' | null);\n stop_sequence: (string | 'null' | null);\n type: 'message';\n usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens: number | null;\n cache_read_input_tokens: number | null;\n };\n };\n /**\n * HTTP status code of response\n */\n statusCode?: number;\n /**\n * User-friendly error message\n */\n errorMessage?: string;\n /**\n * Extra metadata tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"prompt_id\": \"populate-title\" }\n */\n metadata?: Record<string, string>;\n /**\n * Deprecated: use \"metadata\" instead\n */\n tags?: Record<string, (string | number | boolean | 'null' | null)>;\n },\n ): CancelablePromise<{\n status: ('ok' | 'error');\n }> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/report-anthropic',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * @deprecated\n * DEPRECATED: use \"/logs/update-metadata\" instead\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public updateLogTags(\n requestBody: {\n filters: Array<{\n /**\n * The field to filter on. Possible fields include: `model`, `completionId`, and `tags.your_tag_name`.\n */\n field: string;\n equals: (string | number | boolean);\n }>;\n /**\n * Extra tags to attach to the call for filtering. Eg { \"userId\": \"123\", \"prompt_id\": \"populate-title\" }\n */\n tags: Record<string, (string | number | boolean | 'null' | null)>;\n },\n ): CancelablePromise<{\n matchedLogs: number;\n }> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/logs/update-tags',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * Update tags metadata for logged calls matching the provided filters.\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public updateLogMetadata(\n requestBody: {\n filters: Array<{\n /**\n * The field to filter on. Possible fields include: `model`, `completionId`, and `metadata.your_tag_name`.\n */\n field: string;\n equals: (string | number | boolean);\n }>;\n /**\n * Extra metadata to attach to the call for filtering. Eg { \"userId\": \"123\", \"prompt_id\": \"populate-title\" }\n */\n metadata: Record<string, (string | 'null' | null)>;\n },\n ): CancelablePromise<{\n matchedLogs: number;\n }> {\n return this.httpRequest.request({\n method: 'POST',\n url: '/logs/update-metadata',\n body: requestBody,\n mediaType: 'application/json',\n });\n }\n /**\n * Get the latest logged call (only for local testing)\n * @returns any Successful response\n * @throws ApiError\n */\n public localTestingOnlyGetLatestLoggedCall(): CancelablePromise<{\n createdAt: string;\n cacheHit: boolean;\n statusCode: number | null;\n errorMessage: string | null;\n reqPayload?: any;\n respPayload?: any;\n tags: Record<string, string | null>;\n metadata: Record<string, string | null>;\n } | null> {\n return this.httpRequest.request({\n method: 'GET',\n url: '/local-testing-only-get-latest-logged-call',\n });\n }\n /**\n * Get a judgement of a completion against the specified criterion\n * @param requestBody\n * @returns any Successful response\n * @throws ApiError\n */\n public getCriterionJudgement(\n requestBody: {\n /**\n * The ID of the criterion to judge.\n */\n criterion_id: string;\n input?: {\n /**\n * All messages sent to the model when generating the output.\n */\n messages: Array<({\n role: 'system';\n content?: (string | Array<{\n type: 'text';\n text: string;\n }>);\n name?: string;\n } | {\n role: 'user';\n content?: (string | Array<({\n type: 'text';\n text: string;\n } | {\n type: 'image_url';\n image_url: {\n detail?: ('auto' | 'low' | 'high');\n url: string;\n };\n } | {\n type: 'input_audio';\n input_audio: {\n data: string;\n format: 'wav' | 'mp3';\n };\n })>);\n name?: string;\n } | {\n role: 'assistant';\n audio?: {\n id: string;\n } | null;\n content?: (string | Array<({\n type: 'text';\n text: string;\n } | {\n type: 'refusal';\n refusal: string;\n })> | 'null' | null);\n function_call?: {\n name?: string;\n arguments?: string;\n } | null;\n tool_calls?: Array<{\n id: string;\n function: {\n name: string;\n arguments: string;\n };\n type: 'function';\n }> | null;\n name?: string;\n refusal?: string | null;\n annotations?: Array<{\n type: 'url_citation';\n url_citation: {\n start_index: number;\n end_index: number;\n title: string;\n url: string;\n };\n }>;\n } | {\n role: 'developer';\n content?: (string | Array<{\n type: 'text';\n text: string;\n }>);\n name?: string;\n } | {\n role: 'tool';\n content?: (string | Array<{\n type: 'text';\n