"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Client = void 0; const uuid = __importStar(require("uuid")); const async_caller_js_1 = require("./utils/async_caller.cjs"); const messages_js_1 = require("./utils/messages.cjs"); const env_js_1 = require("./utils/env.cjs"); // utility functions const isLocalhost = (url) => { const strippedUrl = url.replace("http://", "").replace("https://", ""); const hostname = strippedUrl.split("/")[0].split(":")[0]; return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1"); }; const raiseForStatus = async (response, operation) => { // consume the response body to release the connection // https://undici.nodejs.org/#/?id=garbage-collection const body = await response.text(); if (!response.ok) { throw new Error(`Failed to ${operation}: ${response.status} ${response.statusText} ${body}`); } }; async function toArray(iterable) { const result = []; for await (const item of iterable) { result.push(item); } return result; } function trimQuotes(str) { if (str === undefined) { return undefined; } return str .trim() .replace(/^"(.*)"$/, "$1") .replace(/^'(.*)'$/, "$1"); } function hideInputs(inputs) { if ((0, env_js_1.getEnvironmentVariable)("LANGCHAIN_HIDE_INPUTS") === "true") { return {}; } return inputs; } function hideOutputs(outputs) { if ((0, env_js_1.getEnvironmentVariable)("LANGCHAIN_HIDE_OUTPUTS") === "true") { return {}; } return outputs; } function assertUuid(str) { if (!uuid.validate(str)) { throw new Error(`Invalid UUID: ${str}`); } } class Client { constructor(config = {}) { Object.defineProperty(this, "apiKey", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "apiUrl", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "webUrl", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "caller", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "timeout_ms", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_tenantId", { enumerable: true, configurable: true, writable: true, value: null }); const defaultConfig = Client.getDefaultClientConfig(); this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? ""; this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey); this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl); this.validateApiKeyIfHosted(); this.timeout_ms = config.timeout_ms ?? 4000; this.caller = new async_caller_js_1.AsyncCaller(config.callerOptions ?? {}); } static getDefaultClientConfig() { const apiKey = (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_API_KEY"); const apiUrl = (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_ENDPOINT") ?? (apiKey ? "https://api.smith.langchain.com" : "http://localhost:1984"); return { apiUrl: apiUrl, apiKey: apiKey, webUrl: undefined, }; } validateApiKeyIfHosted() { const isLocal = isLocalhost(this.apiUrl); if (!isLocal && !this.apiKey) { throw new Error("API key must be provided when using hosted LangSmith API"); } } getHostUrl() { if (this.webUrl) { return this.webUrl; } else if (isLocalhost(this.apiUrl)) { this.webUrl = "http://localhost"; return "http://localhost"; } else if (this.apiUrl.split(".", 1)[0].includes("dev")) { this.webUrl = "https://dev.smith.langchain.com"; return "https://dev.smith.langchain.com"; } else { this.webUrl = "https://smith.langchain.com"; return "https://smith.langchain.com"; } } get headers() { const headers = {}; if (this.apiKey) { headers["x-api-key"] = `${this.apiKey}`; } return headers; } async _getResponse(path, queryParams) { const paramsString = queryParams?.toString() ?? ""; const url = `${this.apiUrl}${path}?${paramsString}`; const response = await this.caller.call(fetch, url, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`); } return response; } async _get(path, queryParams) { const response = await this._getResponse(path, queryParams); return response.json(); } async *_getPaginated(path, queryParams = new URLSearchParams()) { let offset = Number(queryParams.get("offset")) || 0; const limit = Number(queryParams.get("limit")) || 100; while (true) { queryParams.set("offset", String(offset)); queryParams.set("limit", String(limit)); const url = `${this.apiUrl}${path}?${queryParams}`; const response = await this.caller.call(fetch, url, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`); } const items = await response.json(); if (items.length === 0) { break; } yield items; if (items.length < limit) { break; } offset += items.length; } } async createRun(run) { const headers = { ...this.headers, "Content-Type": "application/json" }; const extra = run.extra ?? {}; const runtimeEnv = await (0, env_js_1.getRuntimeEnvironment)(); const session_name = run.project_name; delete run.project_name; const runCreate = { session_name, ...run, extra: { ...run.extra, runtime: { ...runtimeEnv, ...extra.runtime, }, }, }; runCreate.inputs = hideInputs(runCreate.inputs); if (runCreate.outputs) { runCreate.outputs = hideOutputs(runCreate.outputs); } const response = await this.caller.call(fetch, `${this.apiUrl}/runs`, { method: "POST", headers, body: JSON.stringify(runCreate), signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "create run"); } async updateRun(runId, run) { assertUuid(runId); if (run.inputs) { run.inputs = hideInputs(run.inputs); } if (run.outputs) { run.outputs = hideOutputs(run.outputs); } const headers = { ...this.headers, "Content-Type": "application/json" }; const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}`, { method: "PATCH", headers, body: JSON.stringify(run), signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "update run"); } async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) { assertUuid(runId); let run = await this._get(`/runs/${runId}`); if (loadChildRuns && run.child_run_ids) { run = await this._loadChildRuns(run); } return run; } async getRunUrl({ runId, run, projectOpts, }) { if (run !== undefined) { let sessionId; if (run.session_id) { sessionId = run.session_id; } else if (projectOpts?.projectName) { sessionId = (await this.readProject({ projectName: projectOpts?.projectName })).id; } else if (projectOpts?.projectId) { sessionId = projectOpts?.projectId; } else { const project = await this.readProject({ projectName: (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_PROJECT") || "default", }); sessionId = project.id; } const tenantId = await this._getTenantId(); return `${this.getHostUrl()}/o/${tenantId}/projects/p/${sessionId}/r/${run.id}?poll=true`; } else if (runId !== undefined) { const run_ = await this.readRun(runId); if (!run_.app_path) { throw new Error(`Run ${runId} has no app_path`); } const baseUrl = this.getHostUrl(); return `${baseUrl}${run_.app_path}`; } else { throw new Error("Must provide either runId or run"); } } async _loadChildRuns(run) { const childRuns = await toArray(this.listRuns({ id: run.child_run_ids })); const treemap = {}; const runs = {}; // TODO: make dotted order required when the migration finishes childRuns.sort((a, b) => (a?.dotted_order ?? "").localeCompare(b?.dotted_order ?? "")); for (const childRun of childRuns) { if (childRun.parent_run_id === null || childRun.parent_run_id === undefined) { throw new Error(`Child run ${childRun.id} has no parent`); } if (!(childRun.parent_run_id in treemap)) { treemap[childRun.parent_run_id] = []; } treemap[childRun.parent_run_id].push(childRun); runs[childRun.id] = childRun; } run.child_runs = treemap[run.id] || []; for (const runId in treemap) { if (runId !== run.id) { runs[runId].child_runs = treemap[runId]; } } return run; } async *listRuns({ projectId, projectName, parentRunId, referenceExampleId, startTime, executionOrder, runType, error, id, limit, offset, query, filter, }) { const queryParams = new URLSearchParams(); let projectId_ = projectId; if (projectName) { if (projectId) { throw new Error("Only one of projectId or projectName may be given"); } projectId_ = (await this.readProject({ projectName })).id; } if (projectId_) { queryParams.append("session", projectId_); } if (parentRunId) { queryParams.append("parent_run", parentRunId); } if (referenceExampleId) { queryParams.append("reference_example", referenceExampleId); } if (startTime) { queryParams.append("start_time", startTime.toISOString()); } if (executionOrder) { queryParams.append("execution_order", executionOrder.toString()); } if (runType) { queryParams.append("run_type", runType); } if (error !== undefined) { queryParams.append("error", error.toString()); } if (id !== undefined) { for (const id_ of id) { queryParams.append("id", id_); } } if (limit !== undefined) { queryParams.append("limit", limit.toString()); } if (offset !== undefined) { queryParams.append("offset", offset.toString()); } if (query !== undefined) { queryParams.append("query", query); } if (filter !== undefined) { queryParams.append("filter", filter); } for await (const runs of this._getPaginated("/runs", queryParams)) { yield* runs; } } async shareRun(runId, { shareId } = {}) { const data = { run_id: runId, share_token: shareId || uuid.v4(), }; assertUuid(runId); const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}/share`, { method: "PUT", headers: this.headers, body: JSON.stringify(data), signal: AbortSignal.timeout(this.timeout_ms), }); const result = await response.json(); if (result === null || !("share_token" in result)) { throw new Error("Invalid response from server"); } return `${this.getHostUrl()}/public/${result["share_token"]}/r`; } async unshareRun(runId) { assertUuid(runId); const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}/share`, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "unshare run"); } async readRunSharedLink(runId) { assertUuid(runId); const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}/share`, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); const result = await response.json(); if (result === null || !("share_token" in result)) { return undefined; } return `${this.getHostUrl()}/public/${result["share_token"]}/r`; } async listSharedRuns(shareToken, { runIds, } = {}) { const queryParams = new URLSearchParams({ share_token: shareToken, }); if (runIds !== undefined) { for (const runId of runIds) { queryParams.append("id", runId); } } assertUuid(shareToken); const response = await this.caller.call(fetch, `${this.apiUrl}/public/${shareToken}/runs${queryParams}`, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); const runs = await response.json(); return runs; } async readDatasetSharedSchema(datasetId, datasetName) { if (!datasetId && !datasetName) { throw new Error("Either datasetId or datasetName must be given"); } if (!datasetId) { const dataset = await this.readDataset({ datasetName }); datasetId = dataset.id; } assertUuid(datasetId); const response = await this.caller.call(fetch, `${this.apiUrl}/datasets/${datasetId}/share`, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); const shareSchema = await response.json(); shareSchema.url = `${this.getHostUrl()}/public/${shareSchema.share_token}/d`; return shareSchema; } async shareDataset(datasetId, datasetName) { if (!datasetId && !datasetName) { throw new Error("Either datasetId or datasetName must be given"); } if (!datasetId) { const dataset = await this.readDataset({ datasetName }); datasetId = dataset.id; } const data = { dataset_id: datasetId, }; assertUuid(datasetId); const response = await this.caller.call(fetch, `${this.apiUrl}/datasets/${datasetId}/share`, { method: "PUT", headers: this.headers, body: JSON.stringify(data), signal: AbortSignal.timeout(this.timeout_ms), }); const shareSchema = await response.json(); shareSchema.url = `${this.getHostUrl()}/public/${shareSchema.share_token}/d`; return shareSchema; } async unshareDataset(datasetId) { assertUuid(datasetId); const response = await this.caller.call(fetch, `${this.apiUrl}/datasets/${datasetId}/share`, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "unshare dataset"); } async readSharedDataset(shareToken) { assertUuid(shareToken); const response = await this.caller.call(fetch, `${this.apiUrl}/public/${shareToken}/datasets`, { method: "GET", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); const dataset = await response.json(); return dataset; } async createProject({ projectName, projectExtra, upsert, referenceDatasetId, }) { const upsert_ = upsert ? `?upsert=true` : ""; const endpoint = `${this.apiUrl}/sessions${upsert_}`; const body = { name: projectName, }; if (projectExtra !== undefined) { body["extra"] = projectExtra; } if (referenceDatasetId !== undefined) { body["reference_dataset_id"] = referenceDatasetId; } const response = await this.caller.call(fetch, endpoint, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(body), signal: AbortSignal.timeout(this.timeout_ms), }); const result = await response.json(); if (!response.ok) { throw new Error(`Failed to create session ${projectName}: ${response.status} ${response.statusText}`); } return result; } async readProject({ projectId, projectName, }) { let path = "/sessions"; const params = new URLSearchParams(); if (projectId !== undefined && projectName !== undefined) { throw new Error("Must provide either projectName or projectId, not both"); } else if (projectId !== undefined) { assertUuid(projectId); path += `/${projectId}`; } else if (projectName !== undefined) { params.append("name", projectName); } else { throw new Error("Must provide projectName or projectId"); } const response = await this._get(path, params); let result; if (Array.isArray(response)) { if (response.length === 0) { throw new Error(`Project[id=${projectId}, name=${projectName}] not found`); } result = response[0]; } else { result = response; } return result; } async _getTenantId() { if (this._tenantId !== null) { return this._tenantId; } const queryParams = new URLSearchParams({ limit: "1" }); for await (const projects of this._getPaginated("/sessions", queryParams)) { this._tenantId = projects[0].tenant_id; return projects[0].tenant_id; } throw new Error("No projects found to resolve tenant."); } async *listProjects({ projectIds, name, nameContains, referenceDatasetId, referenceDatasetName, referenceFree, } = {}) { const params = new URLSearchParams(); if (projectIds !== undefined) { for (const projectId of projectIds) { params.append("id", projectId); } } if (name !== undefined) { params.append("name", name); } if (nameContains !== undefined) { params.append("name_contains", nameContains); } if (referenceDatasetId !== undefined) { params.append("reference_dataset", referenceDatasetId); } else if (referenceDatasetName !== undefined) { const dataset = await this.readDataset({ datasetName: referenceDatasetName, }); params.append("reference_dataset", dataset.id); } if (referenceFree !== undefined) { params.append("reference_free", referenceFree.toString()); } for await (const projects of this._getPaginated("/sessions", params)) { yield* projects; } } async deleteProject({ projectId, projectName, }) { let projectId_; if (projectId === undefined && projectName === undefined) { throw new Error("Must provide projectName or projectId"); } else if (projectId !== undefined && projectName !== undefined) { throw new Error("Must provide either projectName or projectId, not both"); } else if (projectId === undefined) { projectId_ = (await this.readProject({ projectName })).id; } else { projectId_ = projectId; } assertUuid(projectId_); const response = await this.caller.call(fetch, `${this.apiUrl}/sessions/${projectId_}`, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, `delete session ${projectId_} (${projectName})`); } async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, dataType, name, }) { const url = `${this.apiUrl}/datasets/upload`; const formData = new FormData(); formData.append("file", csvFile, fileName); inputKeys.forEach((key) => { formData.append("input_keys", key); }); outputKeys.forEach((key) => { formData.append("output_keys", key); }); if (description) { formData.append("description", description); } if (dataType) { formData.append("data_type", dataType); } if (name) { formData.append("name", name); } const response = await this.caller.call(fetch, url, { method: "POST", headers: this.headers, body: formData, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { const result = await response.json(); if (result.detail && result.detail.includes("already exists")) { throw new Error(`Dataset ${fileName} already exists`); } throw new Error(`Failed to upload CSV: ${response.status} ${response.statusText}`); } const result = await response.json(); return result; } async createDataset(name, { description, dataType, } = {}) { const body = { name, description, }; if (dataType) { body.data_type = dataType; } const response = await this.caller.call(fetch, `${this.apiUrl}/datasets`, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(body), signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { const result = await response.json(); if (result.detail && result.detail.includes("already exists")) { throw new Error(`Dataset ${name} already exists`); } throw new Error(`Failed to create dataset ${response.status} ${response.statusText}`); } const result = await response.json(); return result; } async readDataset({ datasetId, datasetName, }) { let path = "/datasets"; // limit to 1 result const params = new URLSearchParams({ limit: "1" }); if (datasetId !== undefined && datasetName !== undefined) { throw new Error("Must provide either datasetName or datasetId, not both"); } else if (datasetId !== undefined) { assertUuid(datasetId); path += `/${datasetId}`; } else if (datasetName !== undefined) { params.append("name", datasetName); } else { throw new Error("Must provide datasetName or datasetId"); } const response = await this._get(path, params); let result; if (Array.isArray(response)) { if (response.length === 0) { throw new Error(`Dataset[id=${datasetId}, name=${datasetName}] not found`); } result = response[0]; } else { result = response; } return result; } async readDatasetOpenaiFinetuning({ datasetId, datasetName, }) { const path = "/datasets"; if (datasetId !== undefined) { // do nothing } else if (datasetName !== undefined) { datasetId = (await this.readDataset({ datasetName })).id; } else { throw new Error("Must provide datasetName or datasetId"); } const response = await this._getResponse(`${path}/${datasetId}/openai_ft`); const datasetText = await response.text(); const dataset = datasetText .trim() .split("\n") .map((line) => JSON.parse(line)); return dataset; } async *listDatasets({ limit = 100, offset = 0, datasetIds, datasetName, datasetNameContains, } = {}) { const path = "/datasets"; const params = new URLSearchParams({ limit: limit.toString(), offset: offset.toString(), }); if (datasetIds !== undefined) { for (const id_ of datasetIds) { params.append("id", id_); } } if (datasetName !== undefined) { params.append("name", datasetName); } if (datasetNameContains !== undefined) { params.append("name_contains", datasetNameContains); } for await (const datasets of this._getPaginated(path, params)) { yield* datasets; } } async deleteDataset({ datasetId, datasetName, }) { let path = "/datasets"; let datasetId_ = datasetId; if (datasetId !== undefined && datasetName !== undefined) { throw new Error("Must provide either datasetName or datasetId, not both"); } else if (datasetName !== undefined) { const dataset = await this.readDataset({ datasetName }); datasetId_ = dataset.id; } if (datasetId_ !== undefined) { assertUuid(datasetId_); path += `/${datasetId_}`; } else { throw new Error("Must provide datasetName or datasetId"); } const response = await this.caller.call(fetch, this.apiUrl + path, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`); } await response.json(); } async createExample(inputs, outputs, { datasetId, datasetName, createdAt, exampleId }) { let datasetId_ = datasetId; if (datasetId_ === undefined && datasetName === undefined) { throw new Error("Must provide either datasetName or datasetId"); } else if (datasetId_ !== undefined && datasetName !== undefined) { throw new Error("Must provide either datasetName or datasetId, not both"); } else if (datasetId_ === undefined) { const dataset = await this.readDataset({ datasetName }); datasetId_ = dataset.id; } const createdAt_ = createdAt || new Date(); const data = { dataset_id: datasetId_, inputs, outputs, created_at: createdAt_.toISOString(), id: exampleId, }; const response = await this.caller.call(fetch, `${this.apiUrl}/examples`, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(data), signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to create example: ${response.status} ${response.statusText}`); } const result = await response.json(); return result; } async createLLMExample(input, generation, options) { return this.createExample({ input }, { output: generation }, options); } async createChatExample(input, generations, options) { const finalInput = input.map((message) => { if ((0, messages_js_1.isLangChainMessage)(message)) { return (0, messages_js_1.convertLangChainMessageToExample)(message); } return message; }); const finalOutput = (0, messages_js_1.isLangChainMessage)(generations) ? (0, messages_js_1.convertLangChainMessageToExample)(generations) : generations; return this.createExample({ input: finalInput }, { output: finalOutput }, options); } async readExample(exampleId) { assertUuid(exampleId); const path = `/examples/${exampleId}`; return await this._get(path); } async *listExamples({ datasetId, datasetName, exampleIds, } = {}) { let datasetId_; if (datasetId !== undefined && datasetName !== undefined) { throw new Error("Must provide either datasetName or datasetId, not both"); } else if (datasetId !== undefined) { datasetId_ = datasetId; } else if (datasetName !== undefined) { const dataset = await this.readDataset({ datasetName }); datasetId_ = dataset.id; } else { throw new Error("Must provide a datasetName or datasetId"); } const params = new URLSearchParams({ dataset: datasetId_ }); if (exampleIds !== undefined) { for (const id_ of exampleIds) { params.append("id", id_); } } for await (const examples of this._getPaginated("/examples", params)) { yield* examples; } } async deleteExample(exampleId) { assertUuid(exampleId); const path = `/examples/${exampleId}`; const response = await this.caller.call(fetch, this.apiUrl + path, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`); } await response.json(); } async updateExample(exampleId, update) { assertUuid(exampleId); const response = await this.caller.call(fetch, `${this.apiUrl}/examples/${exampleId}`, { method: "PATCH", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(update), signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to update example ${exampleId}: ${response.status} ${response.statusText}`); } const result = await response.json(); return result; } async evaluateRun(run, evaluator, { sourceInfo, loadChildRuns, } = { loadChildRuns: false }) { let run_; if (typeof run === "string") { run_ = await this.readRun(run, { loadChildRuns }); } else if (typeof run === "object" && "id" in run) { run_ = run; } else { throw new Error(`Invalid run type: ${typeof run}`); } let referenceExample = undefined; if (run_.reference_example_id !== null && run_.reference_example_id !== undefined) { referenceExample = await this.readExample(run_.reference_example_id); } const feedbackResult = await evaluator.evaluateRun(run_, referenceExample); let sourceInfo_ = sourceInfo ?? {}; if (feedbackResult.evaluatorInfo) { sourceInfo_ = { ...sourceInfo_, ...feedbackResult.evaluatorInfo }; } return await this.createFeedback(run_.id, feedbackResult.key, { score: feedbackResult.score, value: feedbackResult.value, comment: feedbackResult.comment, correction: feedbackResult.correction, sourceInfo: sourceInfo_, feedbackSourceType: "model", }); } async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, eager = false, }) { const feedback_source = { type: feedbackSourceType ?? "api", metadata: sourceInfo ?? {}, }; if (sourceRunId !== undefined && feedback_source?.metadata !== undefined && !feedback_source.metadata["__run"]) { feedback_source.metadata["__run"] = { run_id: sourceRunId }; } if (feedback_source?.metadata !== undefined && feedback_source.metadata["__run"]?.run_id !== undefined) { assertUuid(feedback_source.metadata["__run"].run_id); } const feedback = { id: feedbackId ?? uuid.v4(), run_id: runId, key, score, value, correction, comment, feedback_source: feedback_source, }; const url = `${this.apiUrl}/feedback` + (eager ? "/eager" : ""); const response = await this.caller.call(fetch, url, { method: "POST", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(feedback), signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "create feedback"); return feedback; } async updateFeedback(feedbackId, { score, value, correction, comment, }) { const feedbackUpdate = {}; if (score !== undefined && score !== null) { feedbackUpdate["score"] = score; } if (value !== undefined && value !== null) { feedbackUpdate["value"] = value; } if (correction !== undefined && correction !== null) { feedbackUpdate["correction"] = correction; } if (comment !== undefined && comment !== null) { feedbackUpdate["comment"] = comment; } assertUuid(feedbackId); const response = await this.caller.call(fetch, `${this.apiUrl}/feedback/${feedbackId}`, { method: "PATCH", headers: { ...this.headers, "Content-Type": "application/json" }, body: JSON.stringify(feedbackUpdate), signal: AbortSignal.timeout(this.timeout_ms), }); await raiseForStatus(response, "update feedback"); } async readFeedback(feedbackId) { assertUuid(feedbackId); const path = `/feedback/${feedbackId}`; const response = await this._get(path); return response; } async deleteFeedback(feedbackId) { assertUuid(feedbackId); const path = `/feedback/${feedbackId}`; const response = await this.caller.call(fetch, this.apiUrl + path, { method: "DELETE", headers: this.headers, signal: AbortSignal.timeout(this.timeout_ms), }); if (!response.ok) { throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`); } await response.json(); } async *listFeedback({ runIds, feedbackKeys, feedbackSourceTypes, } = {}) { const queryParams = new URLSearchParams(); if (runIds) { queryParams.append("run", runIds.join(",")); } if (feedbackKeys) { for (const key of feedbackKeys) { queryParams.append("key", key); } } if (feedbackSourceTypes) { for (const type of feedbackSourceTypes) { queryParams.append("source", type); } } for await (const feedbacks of this._getPaginated("/feedback", queryParams)) { yield* feedbacks; } } } exports.Client = Client;