langsmith 0.0.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +262 -0
- package/client.cjs +1 -0
- package/client.d.ts +1 -0
- package/client.js +1 -0
- package/dist/cli/docker-compose.ngrok.yaml +17 -0
- package/dist/cli/docker-compose.yaml +43 -0
- package/dist/cli/main.cjs +278 -0
- package/dist/cli/main.d.ts +1 -0
- package/dist/cli/main.js +252 -0
- package/dist/cli/main.ts +292 -0
- package/dist/client.cjs +588 -0
- package/dist/client.d.ts +130 -0
- package/dist/client.js +561 -0
- package/dist/evaluation/evaluator.cjs +2 -0
- package/dist/evaluation/evaluator.d.ts +12 -0
- package/dist/evaluation/evaluator.js +1 -0
- package/dist/evaluation/index.cjs +5 -0
- package/dist/evaluation/index.d.ts +2 -0
- package/dist/evaluation/index.js +1 -0
- package/dist/evaluation/string_evaluator.cjs +66 -0
- package/dist/evaluation/string_evaluator.d.ts +30 -0
- package/dist/evaluation/string_evaluator.js +62 -0
- package/dist/index.cjs +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/run_trees.cjs +232 -0
- package/dist/run_trees.d.ts +47 -0
- package/dist/run_trees.js +205 -0
- package/dist/schemas.cjs +2 -0
- package/dist/schemas.d.ts +117 -0
- package/dist/schemas.js +1 -0
- package/dist/utils/async_caller.cjs +111 -0
- package/dist/utils/async_caller.d.ts +37 -0
- package/dist/utils/async_caller.js +104 -0
- package/dist/utils/env.cjs +80 -0
- package/dist/utils/env.d.ts +22 -0
- package/dist/utils/env.js +68 -0
- package/evaluation.cjs +1 -0
- package/evaluation.d.ts +1 -0
- package/evaluation.js +1 -0
- package/index.cjs +1 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +121 -0
- package/run_trees.cjs +1 -0
- package/run_trees.d.ts +1 -0
- package/run_trees.js +1 -0
- package/schemas.cjs +1 -0
- package/schemas.d.ts +1 -0
- package/schemas.js +1 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { AsyncCallerParams } from "./utils/async_caller.js";
|
|
2
|
+
import { Dataset, Example, ExampleUpdate, Feedback, KVMap, Run, RunCreate, RunType, RunUpdate, ScoreType, TracerSession, TracerSessionResult, ValueType } from "./schemas.js";
|
|
3
|
+
import { RunEvaluator } from "./evaluation/evaluator.js";
|
|
4
|
+
interface ClientConfig {
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
callerOptions?: AsyncCallerParams;
|
|
8
|
+
timeout_ms?: number;
|
|
9
|
+
}
|
|
10
|
+
interface ListRunsParams {
|
|
11
|
+
projectId?: string;
|
|
12
|
+
projectName?: string;
|
|
13
|
+
executionOrder?: number;
|
|
14
|
+
runType?: RunType;
|
|
15
|
+
error?: boolean;
|
|
16
|
+
id?: string[];
|
|
17
|
+
limit?: number;
|
|
18
|
+
offset?: number;
|
|
19
|
+
}
|
|
20
|
+
interface UploadCSVParams {
|
|
21
|
+
csvFile: Blob;
|
|
22
|
+
fileName: string;
|
|
23
|
+
inputKeys: string[];
|
|
24
|
+
outputKeys: string[];
|
|
25
|
+
description?: string;
|
|
26
|
+
}
|
|
27
|
+
interface CreateRunParams {
|
|
28
|
+
name: string;
|
|
29
|
+
inputs: KVMap;
|
|
30
|
+
run_type: RunType;
|
|
31
|
+
execution_order?: number;
|
|
32
|
+
id?: string;
|
|
33
|
+
start_time?: number;
|
|
34
|
+
end_time?: number;
|
|
35
|
+
extra?: KVMap;
|
|
36
|
+
error?: string;
|
|
37
|
+
serialized?: object;
|
|
38
|
+
outputs?: KVMap;
|
|
39
|
+
reference_example_id?: string;
|
|
40
|
+
child_runs?: RunCreate[];
|
|
41
|
+
parent_run_id?: string;
|
|
42
|
+
project_name?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare class Client {
|
|
45
|
+
private apiKey?;
|
|
46
|
+
private apiUrl;
|
|
47
|
+
private caller;
|
|
48
|
+
private timeout_ms;
|
|
49
|
+
constructor(config?: ClientConfig);
|
|
50
|
+
static getDefaultClientConfig(): {
|
|
51
|
+
apiUrl: string;
|
|
52
|
+
apiKey?: string;
|
|
53
|
+
};
|
|
54
|
+
private validateApiKeyIfHosted;
|
|
55
|
+
private get headers();
|
|
56
|
+
private _get;
|
|
57
|
+
createRun(run: CreateRunParams): Promise<void>;
|
|
58
|
+
updateRun(runId: string, run: RunUpdate): Promise<void>;
|
|
59
|
+
readRun(runId: string, { loadChildRuns }?: {
|
|
60
|
+
loadChildRuns: boolean;
|
|
61
|
+
}): Promise<Run>;
|
|
62
|
+
private _loadChildRuns;
|
|
63
|
+
listRuns({ projectId, projectName, executionOrder, runType, error, id, limit, offset, }: ListRunsParams): Promise<Run[]>;
|
|
64
|
+
deleteRun(runId: string): Promise<void>;
|
|
65
|
+
createProject({ projectName, projectExtra, mode, upsert, }: {
|
|
66
|
+
projectName: string;
|
|
67
|
+
projectExtra?: object;
|
|
68
|
+
mode?: string;
|
|
69
|
+
upsert?: boolean;
|
|
70
|
+
}): Promise<TracerSession>;
|
|
71
|
+
readProject({ projectId, projectName, }: {
|
|
72
|
+
projectId?: string;
|
|
73
|
+
projectName?: string;
|
|
74
|
+
}): Promise<TracerSessionResult>;
|
|
75
|
+
listProjects(): Promise<TracerSession[]>;
|
|
76
|
+
deleteProject({ projectId, projectName, }: {
|
|
77
|
+
projectId?: string;
|
|
78
|
+
projectName?: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, }: UploadCSVParams): Promise<Dataset>;
|
|
81
|
+
createDataset(name: string, { description }?: {
|
|
82
|
+
description?: string;
|
|
83
|
+
}): Promise<Dataset>;
|
|
84
|
+
readDataset({ datasetId, datasetName, }: {
|
|
85
|
+
datasetId?: string;
|
|
86
|
+
datasetName?: string;
|
|
87
|
+
}): Promise<Dataset>;
|
|
88
|
+
listDatasets({ limit, offset, }?: {
|
|
89
|
+
limit?: number;
|
|
90
|
+
offset?: number;
|
|
91
|
+
}): Promise<Dataset[]>;
|
|
92
|
+
deleteDataset({ datasetId, datasetName, }: {
|
|
93
|
+
datasetId?: string;
|
|
94
|
+
datasetName?: string;
|
|
95
|
+
}): Promise<Dataset>;
|
|
96
|
+
createExample(inputs: KVMap, outputs: KVMap, { datasetId, datasetName, createdAt, }: {
|
|
97
|
+
datasetId?: string;
|
|
98
|
+
datasetName?: string;
|
|
99
|
+
createdAt?: Date;
|
|
100
|
+
}): Promise<Example>;
|
|
101
|
+
readExample(exampleId: string): Promise<Example>;
|
|
102
|
+
listExamples({ datasetId, datasetName, limit, offset, }?: {
|
|
103
|
+
datasetId?: string;
|
|
104
|
+
datasetName?: string;
|
|
105
|
+
limit?: number;
|
|
106
|
+
offset?: number;
|
|
107
|
+
}): Promise<Example[]>;
|
|
108
|
+
deleteExample(exampleId: string): Promise<Example>;
|
|
109
|
+
updateExample(exampleId: string, update: ExampleUpdate): Promise<object>;
|
|
110
|
+
evaluateRun(run: Run | string, evaluator: RunEvaluator, { sourceInfo, loadChildRuns, }?: {
|
|
111
|
+
sourceInfo?: KVMap;
|
|
112
|
+
loadChildRuns: boolean;
|
|
113
|
+
}): Promise<Feedback>;
|
|
114
|
+
createFeedback(runId: string, key: string, { score, value, correction, comment, sourceInfo, feedbackSourceType, }: {
|
|
115
|
+
score?: ScoreType;
|
|
116
|
+
value?: ValueType;
|
|
117
|
+
correction?: string | object;
|
|
118
|
+
comment?: string;
|
|
119
|
+
sourceInfo?: object;
|
|
120
|
+
feedbackSourceType?: "API" | "MODEL";
|
|
121
|
+
}): Promise<Feedback>;
|
|
122
|
+
readFeedback(feedbackId: string): Promise<Feedback>;
|
|
123
|
+
deleteFeedback(feedbackId: string): Promise<Feedback>;
|
|
124
|
+
listFeedback({ runIds, limit, offset, }?: {
|
|
125
|
+
runIds?: string[];
|
|
126
|
+
limit?: number;
|
|
127
|
+
offset?: number;
|
|
128
|
+
}): Promise<Feedback[]>;
|
|
129
|
+
}
|
|
130
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import * as uuid from "uuid";
|
|
2
|
+
import { AsyncCaller } from "./utils/async_caller.js";
|
|
3
|
+
import { getEnvironmentVariable, getRuntimeEnvironment } from "./utils/env.js";
|
|
4
|
+
// utility functions
|
|
5
|
+
const isLocalhost = (url) => {
|
|
6
|
+
const strippedUrl = url.replace("http://", "").replace("https://", "");
|
|
7
|
+
const hostname = strippedUrl.split("/")[0].split(":")[0];
|
|
8
|
+
return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
|
|
9
|
+
};
|
|
10
|
+
const raiseForStatus = async (response, operation) => {
|
|
11
|
+
// consume the response body to release the connection
|
|
12
|
+
// https://undici.nodejs.org/#/?id=garbage-collection
|
|
13
|
+
const body = await response.text();
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(`Failed to ${operation}: ${response.status} ${response.statusText} ${body}`);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export class Client {
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
Object.defineProperty(this, "apiKey", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "apiUrl", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "caller", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "timeout_ms", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
const defaultConfig = Client.getDefaultClientConfig();
|
|
45
|
+
this.apiUrl = config.apiUrl ?? defaultConfig.apiUrl;
|
|
46
|
+
this.apiKey = config.apiKey ?? defaultConfig.apiKey;
|
|
47
|
+
this.validateApiKeyIfHosted();
|
|
48
|
+
this.timeout_ms = config.timeout_ms ?? 4000;
|
|
49
|
+
this.caller = new AsyncCaller(config.callerOptions ?? {});
|
|
50
|
+
}
|
|
51
|
+
static getDefaultClientConfig() {
|
|
52
|
+
return {
|
|
53
|
+
apiUrl: getEnvironmentVariable("LANGCHAIN_ENDPOINT") ?? "http://localhost:1984",
|
|
54
|
+
apiKey: getEnvironmentVariable("LANGCHAIN_API_KEY"),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
validateApiKeyIfHosted() {
|
|
58
|
+
const isLocal = isLocalhost(this.apiUrl);
|
|
59
|
+
if (!isLocal && !this.apiKey) {
|
|
60
|
+
throw new Error("API key must be provided when using hosted LangChain+ API");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
get headers() {
|
|
64
|
+
const headers = {};
|
|
65
|
+
if (this.apiKey) {
|
|
66
|
+
headers["x-api-key"] = `${this.apiKey}`;
|
|
67
|
+
}
|
|
68
|
+
return headers;
|
|
69
|
+
}
|
|
70
|
+
async _get(path, queryParams) {
|
|
71
|
+
const paramsString = queryParams?.toString() ?? "";
|
|
72
|
+
const url = `${this.apiUrl}${path}?${paramsString}`;
|
|
73
|
+
const response = await this.caller.call(fetch, url, {
|
|
74
|
+
method: "GET",
|
|
75
|
+
headers: this.headers,
|
|
76
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
return response.json();
|
|
82
|
+
}
|
|
83
|
+
async createRun(run) {
|
|
84
|
+
const headers = { ...this.headers, "Content-Type": "application/json" };
|
|
85
|
+
const extra = run.extra ?? {};
|
|
86
|
+
const runtimeEnv = await getRuntimeEnvironment();
|
|
87
|
+
const session_name = run.project_name;
|
|
88
|
+
delete run.project_name;
|
|
89
|
+
const runCreate = {
|
|
90
|
+
session_name,
|
|
91
|
+
...run,
|
|
92
|
+
extra: {
|
|
93
|
+
...run.extra,
|
|
94
|
+
runtime: {
|
|
95
|
+
...runtimeEnv,
|
|
96
|
+
...extra.runtime,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/runs`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers,
|
|
103
|
+
body: JSON.stringify(runCreate),
|
|
104
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
105
|
+
});
|
|
106
|
+
await raiseForStatus(response, "create run");
|
|
107
|
+
}
|
|
108
|
+
async updateRun(runId, run) {
|
|
109
|
+
const headers = { ...this.headers, "Content-Type": "application/json" };
|
|
110
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}`, {
|
|
111
|
+
method: "PATCH",
|
|
112
|
+
headers,
|
|
113
|
+
body: JSON.stringify(run),
|
|
114
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
115
|
+
});
|
|
116
|
+
await raiseForStatus(response, "update run");
|
|
117
|
+
}
|
|
118
|
+
async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) {
|
|
119
|
+
let run = await this._get(`/runs/${runId}`);
|
|
120
|
+
if (loadChildRuns && run.child_run_ids) {
|
|
121
|
+
run = await this._loadChildRuns(run);
|
|
122
|
+
}
|
|
123
|
+
return run;
|
|
124
|
+
}
|
|
125
|
+
async _loadChildRuns(run) {
|
|
126
|
+
const childRuns = await this.listRuns({ id: run.child_run_ids });
|
|
127
|
+
const treemap = {};
|
|
128
|
+
const runs = {};
|
|
129
|
+
childRuns.sort((a, b) => a.execution_order - b.execution_order);
|
|
130
|
+
for (const childRun of childRuns) {
|
|
131
|
+
if (childRun.parent_run_id === null ||
|
|
132
|
+
childRun.parent_run_id === undefined) {
|
|
133
|
+
throw new Error(`Child run ${childRun.id} has no parent`);
|
|
134
|
+
}
|
|
135
|
+
if (!(childRun.parent_run_id in treemap)) {
|
|
136
|
+
treemap[childRun.parent_run_id] = [];
|
|
137
|
+
}
|
|
138
|
+
treemap[childRun.parent_run_id].push(childRun);
|
|
139
|
+
runs[childRun.id] = childRun;
|
|
140
|
+
}
|
|
141
|
+
run.child_runs = treemap[run.id] || [];
|
|
142
|
+
for (const runId in treemap) {
|
|
143
|
+
if (runId !== run.id) {
|
|
144
|
+
runs[runId].child_runs = treemap[runId];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return run;
|
|
148
|
+
}
|
|
149
|
+
async listRuns({ projectId, projectName, executionOrder, runType, error, id, limit, offset, }) {
|
|
150
|
+
const queryParams = new URLSearchParams();
|
|
151
|
+
let projectId_ = projectId;
|
|
152
|
+
if (projectName) {
|
|
153
|
+
if (projectId) {
|
|
154
|
+
throw new Error("Only one of projectId or projectName may be given");
|
|
155
|
+
}
|
|
156
|
+
projectId_ = (await this.readProject({ projectName })).id;
|
|
157
|
+
}
|
|
158
|
+
if (projectId_) {
|
|
159
|
+
queryParams.append("session", projectId_);
|
|
160
|
+
}
|
|
161
|
+
if (executionOrder) {
|
|
162
|
+
queryParams.append("execution_order", executionOrder.toString());
|
|
163
|
+
}
|
|
164
|
+
if (runType) {
|
|
165
|
+
queryParams.append("run_type", runType);
|
|
166
|
+
}
|
|
167
|
+
if (error !== undefined) {
|
|
168
|
+
queryParams.append("error", error.toString());
|
|
169
|
+
}
|
|
170
|
+
if (id !== undefined) {
|
|
171
|
+
for (const id_ of id) {
|
|
172
|
+
queryParams.append("id", id_);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (limit !== undefined) {
|
|
176
|
+
queryParams.append("limit", limit.toString());
|
|
177
|
+
}
|
|
178
|
+
if (offset !== undefined) {
|
|
179
|
+
queryParams.append("offset", offset.toString());
|
|
180
|
+
}
|
|
181
|
+
return this._get("/runs", queryParams);
|
|
182
|
+
}
|
|
183
|
+
async deleteRun(runId) {
|
|
184
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}`, {
|
|
185
|
+
method: "DELETE",
|
|
186
|
+
headers: this.headers,
|
|
187
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
188
|
+
});
|
|
189
|
+
await raiseForStatus(response, "delete run");
|
|
190
|
+
}
|
|
191
|
+
async createProject({ projectName, projectExtra, mode, upsert, }) {
|
|
192
|
+
const upsert_ = upsert ? `?upsert=true` : "";
|
|
193
|
+
const endpoint = `${this.apiUrl}/sessions${upsert_}`;
|
|
194
|
+
const body = {
|
|
195
|
+
name: projectName,
|
|
196
|
+
};
|
|
197
|
+
if (projectExtra !== undefined) {
|
|
198
|
+
body["extra"] = projectExtra;
|
|
199
|
+
}
|
|
200
|
+
if (mode !== undefined) {
|
|
201
|
+
body["mode"] = mode;
|
|
202
|
+
}
|
|
203
|
+
const response = await this.caller.call(fetch, endpoint, {
|
|
204
|
+
method: "POST",
|
|
205
|
+
headers: { ...this.headers, "Content-Type": "application/json" },
|
|
206
|
+
body: JSON.stringify(body),
|
|
207
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
208
|
+
});
|
|
209
|
+
const result = await response.json();
|
|
210
|
+
if (!response.ok) {
|
|
211
|
+
throw new Error(`Failed to create session ${projectName}: ${response.status} ${response.statusText}`);
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
async readProject({ projectId, projectName, }) {
|
|
216
|
+
let path = "/sessions";
|
|
217
|
+
const params = new URLSearchParams();
|
|
218
|
+
if (projectId !== undefined && projectName !== undefined) {
|
|
219
|
+
throw new Error("Must provide either projectName or projectId, not both");
|
|
220
|
+
}
|
|
221
|
+
else if (projectId !== undefined) {
|
|
222
|
+
path += `/${projectId}`;
|
|
223
|
+
}
|
|
224
|
+
else if (projectName !== undefined) {
|
|
225
|
+
params.append("name", projectName);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
throw new Error("Must provide projectName or projectId");
|
|
229
|
+
}
|
|
230
|
+
const response = await this._get(path, params);
|
|
231
|
+
let result;
|
|
232
|
+
if (Array.isArray(response)) {
|
|
233
|
+
if (response.length === 0) {
|
|
234
|
+
throw new Error(`Project[id=${projectId}, name=${projectName}] not found`);
|
|
235
|
+
}
|
|
236
|
+
result = response[0];
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
result = response;
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
async listProjects() {
|
|
244
|
+
return this._get("/sessions");
|
|
245
|
+
}
|
|
246
|
+
async deleteProject({ projectId, projectName, }) {
|
|
247
|
+
let projectId_;
|
|
248
|
+
if (projectId === undefined && projectName === undefined) {
|
|
249
|
+
throw new Error("Must provide projectName or projectId");
|
|
250
|
+
}
|
|
251
|
+
else if (projectId !== undefined && projectName !== undefined) {
|
|
252
|
+
throw new Error("Must provide either projectName or projectId, not both");
|
|
253
|
+
}
|
|
254
|
+
else if (projectId === undefined) {
|
|
255
|
+
projectId_ = (await this.readProject({ projectName })).id;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
projectId_ = projectId;
|
|
259
|
+
}
|
|
260
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/sessions/${projectId_}`, {
|
|
261
|
+
method: "DELETE",
|
|
262
|
+
headers: this.headers,
|
|
263
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
264
|
+
});
|
|
265
|
+
await raiseForStatus(response, `delete session ${projectId_} (${projectName})`);
|
|
266
|
+
}
|
|
267
|
+
async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, }) {
|
|
268
|
+
const url = `${this.apiUrl}/datasets/upload`;
|
|
269
|
+
const formData = new FormData();
|
|
270
|
+
formData.append("file", csvFile, fileName);
|
|
271
|
+
formData.append("input_keys", inputKeys.join(","));
|
|
272
|
+
formData.append("output_keys", outputKeys.join(","));
|
|
273
|
+
if (description) {
|
|
274
|
+
formData.append("description", description);
|
|
275
|
+
}
|
|
276
|
+
const response = await this.caller.call(fetch, url, {
|
|
277
|
+
method: "POST",
|
|
278
|
+
headers: this.headers,
|
|
279
|
+
body: formData,
|
|
280
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
281
|
+
});
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
const result = await response.json();
|
|
284
|
+
if (result.detail && result.detail.includes("already exists")) {
|
|
285
|
+
throw new Error(`Dataset ${fileName} already exists`);
|
|
286
|
+
}
|
|
287
|
+
throw new Error(`Failed to upload CSV: ${response.status} ${response.statusText}`);
|
|
288
|
+
}
|
|
289
|
+
const result = await response.json();
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
async createDataset(name, { description } = {}) {
|
|
293
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/datasets`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: { ...this.headers, "Content-Type": "application/json" },
|
|
296
|
+
body: JSON.stringify({
|
|
297
|
+
name,
|
|
298
|
+
description,
|
|
299
|
+
}),
|
|
300
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
301
|
+
});
|
|
302
|
+
if (!response.ok) {
|
|
303
|
+
const result = await response.json();
|
|
304
|
+
if (result.detail && result.detail.includes("already exists")) {
|
|
305
|
+
throw new Error(`Dataset ${name} already exists`);
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Failed to create dataset ${response.status} ${response.statusText}`);
|
|
308
|
+
}
|
|
309
|
+
const result = await response.json();
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
async readDataset({ datasetId, datasetName, }) {
|
|
313
|
+
let path = "/datasets";
|
|
314
|
+
// limit to 1 result
|
|
315
|
+
const params = new URLSearchParams({ limit: "1" });
|
|
316
|
+
if (datasetId !== undefined && datasetName !== undefined) {
|
|
317
|
+
throw new Error("Must provide either datasetName or datasetId, not both");
|
|
318
|
+
}
|
|
319
|
+
else if (datasetId !== undefined) {
|
|
320
|
+
path += `/${datasetId}`;
|
|
321
|
+
}
|
|
322
|
+
else if (datasetName !== undefined) {
|
|
323
|
+
params.append("name", datasetName);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
throw new Error("Must provide datasetName or datasetId");
|
|
327
|
+
}
|
|
328
|
+
const response = await this._get(path, params);
|
|
329
|
+
let result;
|
|
330
|
+
if (Array.isArray(response)) {
|
|
331
|
+
if (response.length === 0) {
|
|
332
|
+
throw new Error(`Dataset[id=${datasetId}, name=${datasetName}] not found`);
|
|
333
|
+
}
|
|
334
|
+
result = response[0];
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
result = response;
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
async listDatasets({ limit = 100, offset = 0, } = {}) {
|
|
342
|
+
const path = "/datasets";
|
|
343
|
+
const params = new URLSearchParams({
|
|
344
|
+
limit: limit.toString(),
|
|
345
|
+
offset: offset.toString(),
|
|
346
|
+
});
|
|
347
|
+
const response = await this._get(path, params);
|
|
348
|
+
if (!Array.isArray(response)) {
|
|
349
|
+
throw new Error(`Expected ${path} to return an array, but got ${response}`);
|
|
350
|
+
}
|
|
351
|
+
return response;
|
|
352
|
+
}
|
|
353
|
+
async deleteDataset({ datasetId, datasetName, }) {
|
|
354
|
+
let path = "/datasets";
|
|
355
|
+
let datasetId_ = datasetId;
|
|
356
|
+
if (datasetId !== undefined && datasetName !== undefined) {
|
|
357
|
+
throw new Error("Must provide either datasetName or datasetId, not both");
|
|
358
|
+
}
|
|
359
|
+
else if (datasetName !== undefined) {
|
|
360
|
+
const dataset = await this.readDataset({ datasetName });
|
|
361
|
+
datasetId_ = dataset.id;
|
|
362
|
+
}
|
|
363
|
+
if (datasetId_ !== undefined) {
|
|
364
|
+
path += `/${datasetId_}`;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
throw new Error("Must provide datasetName or datasetId");
|
|
368
|
+
}
|
|
369
|
+
const response = await this.caller.call(fetch, this.apiUrl + path, {
|
|
370
|
+
method: "DELETE",
|
|
371
|
+
headers: this.headers,
|
|
372
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
373
|
+
});
|
|
374
|
+
if (!response.ok) {
|
|
375
|
+
throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
|
|
376
|
+
}
|
|
377
|
+
const results = await response.json();
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
async createExample(inputs, outputs, { datasetId, datasetName, createdAt, }) {
|
|
381
|
+
let datasetId_ = datasetId;
|
|
382
|
+
if (datasetId_ === undefined && datasetName === undefined) {
|
|
383
|
+
throw new Error("Must provide either datasetName or datasetId");
|
|
384
|
+
}
|
|
385
|
+
else if (datasetId_ !== undefined && datasetName !== undefined) {
|
|
386
|
+
throw new Error("Must provide either datasetName or datasetId, not both");
|
|
387
|
+
}
|
|
388
|
+
else if (datasetId_ === undefined) {
|
|
389
|
+
const dataset = await this.readDataset({ datasetName });
|
|
390
|
+
datasetId_ = dataset.id;
|
|
391
|
+
}
|
|
392
|
+
const createdAt_ = createdAt || new Date();
|
|
393
|
+
const data = {
|
|
394
|
+
dataset_id: datasetId_,
|
|
395
|
+
inputs,
|
|
396
|
+
outputs,
|
|
397
|
+
created_at: createdAt_.toISOString(),
|
|
398
|
+
};
|
|
399
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/examples`, {
|
|
400
|
+
method: "POST",
|
|
401
|
+
headers: { ...this.headers, "Content-Type": "application/json" },
|
|
402
|
+
body: JSON.stringify(data),
|
|
403
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
404
|
+
});
|
|
405
|
+
if (!response.ok) {
|
|
406
|
+
throw new Error(`Failed to create example: ${response.status} ${response.statusText}`);
|
|
407
|
+
}
|
|
408
|
+
const result = await response.json();
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
async readExample(exampleId) {
|
|
412
|
+
const path = `/examples/${exampleId}`;
|
|
413
|
+
return await this._get(path);
|
|
414
|
+
}
|
|
415
|
+
async listExamples({ datasetId, datasetName, limit, offset, } = {}) {
|
|
416
|
+
let datasetId_;
|
|
417
|
+
if (datasetId !== undefined && datasetName !== undefined) {
|
|
418
|
+
throw new Error("Must provide either datasetName or datasetId, not both");
|
|
419
|
+
}
|
|
420
|
+
else if (datasetId !== undefined) {
|
|
421
|
+
datasetId_ = datasetId;
|
|
422
|
+
}
|
|
423
|
+
else if (datasetName !== undefined) {
|
|
424
|
+
const dataset = await this.readDataset({ datasetName });
|
|
425
|
+
datasetId_ = dataset.id;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
throw new Error("Must provide a datasetName or datasetId");
|
|
429
|
+
}
|
|
430
|
+
const response = await this._get("/examples", new URLSearchParams({
|
|
431
|
+
dataset: datasetId_,
|
|
432
|
+
limit: limit?.toString() ?? "100",
|
|
433
|
+
offset: offset?.toString() ?? "0",
|
|
434
|
+
}));
|
|
435
|
+
if (!Array.isArray(response)) {
|
|
436
|
+
throw new Error(`Expected /examples to return an array, but got ${response}`);
|
|
437
|
+
}
|
|
438
|
+
return response;
|
|
439
|
+
}
|
|
440
|
+
async deleteExample(exampleId) {
|
|
441
|
+
const path = `/examples/${exampleId}`;
|
|
442
|
+
const response = await this.caller.call(fetch, this.apiUrl + path, {
|
|
443
|
+
method: "DELETE",
|
|
444
|
+
headers: this.headers,
|
|
445
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
446
|
+
});
|
|
447
|
+
if (!response.ok) {
|
|
448
|
+
throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
|
|
449
|
+
}
|
|
450
|
+
const result = await response.json();
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
async updateExample(exampleId, update) {
|
|
454
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/examples/${exampleId}`, {
|
|
455
|
+
method: "PATCH",
|
|
456
|
+
headers: { ...this.headers, "Content-Type": "application/json" },
|
|
457
|
+
body: JSON.stringify(update),
|
|
458
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
459
|
+
});
|
|
460
|
+
if (!response.ok) {
|
|
461
|
+
throw new Error(`Failed to update example ${exampleId}: ${response.status} ${response.statusText}`);
|
|
462
|
+
}
|
|
463
|
+
const result = await response.json();
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
async evaluateRun(run, evaluator, { sourceInfo, loadChildRuns, } = { loadChildRuns: false }) {
|
|
467
|
+
let run_;
|
|
468
|
+
if (typeof run === "string") {
|
|
469
|
+
run_ = await this.readRun(run, { loadChildRuns });
|
|
470
|
+
}
|
|
471
|
+
else if (typeof run === "object" && "id" in run) {
|
|
472
|
+
run_ = run;
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
throw new Error(`Invalid run type: ${typeof run}`);
|
|
476
|
+
}
|
|
477
|
+
let referenceExample = undefined;
|
|
478
|
+
if (run_.reference_example_id !== null &&
|
|
479
|
+
run_.reference_example_id !== undefined) {
|
|
480
|
+
referenceExample = await this.readExample(run_.reference_example_id);
|
|
481
|
+
}
|
|
482
|
+
const feedbackResult = await evaluator.evaluateRun(run_, referenceExample);
|
|
483
|
+
let sourceInfo_ = sourceInfo ?? {};
|
|
484
|
+
if (feedbackResult.evaluatorInfo) {
|
|
485
|
+
sourceInfo_ = { ...sourceInfo_, ...feedbackResult.evaluatorInfo };
|
|
486
|
+
}
|
|
487
|
+
return await this.createFeedback(run_.id, feedbackResult.key, {
|
|
488
|
+
score: feedbackResult.score,
|
|
489
|
+
value: feedbackResult.value,
|
|
490
|
+
comment: feedbackResult.comment,
|
|
491
|
+
correction: feedbackResult.correction,
|
|
492
|
+
sourceInfo: sourceInfo_,
|
|
493
|
+
feedbackSourceType: "MODEL",
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "API", }) {
|
|
497
|
+
let feedback_source;
|
|
498
|
+
if (feedbackSourceType === "API") {
|
|
499
|
+
feedback_source = { type: "api", metadata: sourceInfo ?? {} };
|
|
500
|
+
}
|
|
501
|
+
else if (feedbackSourceType === "MODEL") {
|
|
502
|
+
feedback_source = { type: "model", metadata: sourceInfo ?? {} };
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
throw new Error(`Unknown feedback source type ${feedbackSourceType}`);
|
|
506
|
+
}
|
|
507
|
+
const feedback = {
|
|
508
|
+
id: uuid.v4(),
|
|
509
|
+
run_id: runId,
|
|
510
|
+
key,
|
|
511
|
+
score,
|
|
512
|
+
value,
|
|
513
|
+
correction,
|
|
514
|
+
comment,
|
|
515
|
+
feedback_source: feedback_source,
|
|
516
|
+
};
|
|
517
|
+
const response = await this.caller.call(fetch, `${this.apiUrl}/feedback`, {
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: { ...this.headers, "Content-Type": "application/json" },
|
|
520
|
+
body: JSON.stringify(feedback),
|
|
521
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
522
|
+
});
|
|
523
|
+
if (!response.ok) {
|
|
524
|
+
throw new Error(`Failed to create feedback for run ${runId}: ${response.status} ${response.statusText}`);
|
|
525
|
+
}
|
|
526
|
+
const result = await response.json();
|
|
527
|
+
return result;
|
|
528
|
+
}
|
|
529
|
+
async readFeedback(feedbackId) {
|
|
530
|
+
const path = `/feedback/${feedbackId}`;
|
|
531
|
+
const response = await this._get(path);
|
|
532
|
+
return response;
|
|
533
|
+
}
|
|
534
|
+
async deleteFeedback(feedbackId) {
|
|
535
|
+
const path = `/feedback/${feedbackId}`;
|
|
536
|
+
const response = await this.caller.call(fetch, this.apiUrl + path, {
|
|
537
|
+
method: "DELETE",
|
|
538
|
+
headers: this.headers,
|
|
539
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
540
|
+
});
|
|
541
|
+
if (!response.ok) {
|
|
542
|
+
throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
|
|
543
|
+
}
|
|
544
|
+
const result = await response.json();
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
async listFeedback({ runIds, limit, offset, } = {}) {
|
|
548
|
+
const queryParams = new URLSearchParams();
|
|
549
|
+
if (runIds) {
|
|
550
|
+
queryParams.append("run", runIds.join(","));
|
|
551
|
+
}
|
|
552
|
+
if (limit !== undefined) {
|
|
553
|
+
queryParams.append("limit", limit.toString());
|
|
554
|
+
}
|
|
555
|
+
if (offset !== undefined) {
|
|
556
|
+
queryParams.append("offset", offset.toString());
|
|
557
|
+
}
|
|
558
|
+
const response = await this._get("/feedback", queryParams);
|
|
559
|
+
return response;
|
|
560
|
+
}
|
|
561
|
+
}
|