@promptmetrics/sdk 1.0.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/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +854 -0
- package/dist/index.d.mts +1320 -0
- package/dist/index.d.ts +1320 -0
- package/dist/index.js +1373 -0
- package/dist/index.mjs +1326 -0
- package/package.json +86 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1326 @@
|
|
|
1
|
+
// src/utils/http.ts
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
// src/utils/errors.ts
|
|
5
|
+
var PromptMetricsError = class extends Error {
|
|
6
|
+
constructor(message, statusCode, code, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "PromptMetricsError";
|
|
9
|
+
this.statusCode = statusCode;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.details = details;
|
|
12
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
13
|
+
Error.captureStackTrace(this, this.constructor);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Custom JSON serialization to include message field
|
|
18
|
+
*/
|
|
19
|
+
toJSON() {
|
|
20
|
+
const result = {
|
|
21
|
+
name: this.name,
|
|
22
|
+
message: this.message,
|
|
23
|
+
statusCode: this.statusCode,
|
|
24
|
+
code: this.code
|
|
25
|
+
};
|
|
26
|
+
if (this.details !== void 0) {
|
|
27
|
+
result.details = this.details;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var AuthenticationError = class extends PromptMetricsError {
|
|
33
|
+
constructor(message = "Invalid API key or authentication failed") {
|
|
34
|
+
super(message, 401, "AUTHENTICATION_ERROR");
|
|
35
|
+
this.name = "AuthenticationError";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var AuthorizationError = class extends PromptMetricsError {
|
|
39
|
+
constructor(message = "You do not have permission to access this resource") {
|
|
40
|
+
super(message, 403, "AUTHORIZATION_ERROR");
|
|
41
|
+
this.name = "AuthorizationError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var NotFoundError = class extends PromptMetricsError {
|
|
45
|
+
constructor(message = "Resource not found") {
|
|
46
|
+
super(message, 404, "NOT_FOUND_ERROR");
|
|
47
|
+
this.name = "NotFoundError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var ValidationError = class extends PromptMetricsError {
|
|
51
|
+
constructor(message = "Invalid request parameters", details) {
|
|
52
|
+
super(message, 400, "VALIDATION_ERROR", details);
|
|
53
|
+
this.name = "ValidationError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var RateLimitError = class extends PromptMetricsError {
|
|
57
|
+
constructor(message = "Rate limit exceeded") {
|
|
58
|
+
super(message, 429, "RATE_LIMIT_ERROR");
|
|
59
|
+
this.name = "RateLimitError";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var ServerError = class extends PromptMetricsError {
|
|
63
|
+
constructor(message = "Internal server error") {
|
|
64
|
+
super(message, 500, "SERVER_ERROR");
|
|
65
|
+
this.name = "ServerError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var NetworkError = class extends PromptMetricsError {
|
|
69
|
+
constructor(message = "Network request failed") {
|
|
70
|
+
super(message, void 0, "NETWORK_ERROR");
|
|
71
|
+
this.name = "NetworkError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var TimeoutError = class extends PromptMetricsError {
|
|
75
|
+
constructor(message = "Request timeout") {
|
|
76
|
+
super(message, 408, "TIMEOUT_ERROR");
|
|
77
|
+
this.name = "TimeoutError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/utils/http.ts
|
|
82
|
+
var HttpClient = class {
|
|
83
|
+
constructor(config) {
|
|
84
|
+
this.maxRetries = config.maxRetries || 3;
|
|
85
|
+
this.debug = config.debug || false;
|
|
86
|
+
this.client = axios.create({
|
|
87
|
+
baseURL: config.baseURL,
|
|
88
|
+
timeout: config.timeout || 3e4,
|
|
89
|
+
headers: {
|
|
90
|
+
"Authorization": `Bearer ${config.apiKey}`,
|
|
91
|
+
"Content-Type": "application/json"
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
if (this.debug) {
|
|
95
|
+
this.client.interceptors.request.use((request) => {
|
|
96
|
+
console.log("[PromptMetrics SDK] Request:", {
|
|
97
|
+
method: request.method,
|
|
98
|
+
url: request.url,
|
|
99
|
+
params: request.params,
|
|
100
|
+
data: request.data
|
|
101
|
+
});
|
|
102
|
+
return request;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (this.debug) {
|
|
106
|
+
this.client.interceptors.response.use(
|
|
107
|
+
(response) => {
|
|
108
|
+
console.log("[PromptMetrics SDK] Response:", {
|
|
109
|
+
status: response.status,
|
|
110
|
+
data: response.data
|
|
111
|
+
});
|
|
112
|
+
return response;
|
|
113
|
+
},
|
|
114
|
+
(error) => {
|
|
115
|
+
console.error("[PromptMetrics SDK] Error:", error);
|
|
116
|
+
return Promise.reject(error);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Make HTTP request with retry logic
|
|
123
|
+
*/
|
|
124
|
+
async request(config) {
|
|
125
|
+
let lastError;
|
|
126
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
127
|
+
try {
|
|
128
|
+
const response = await this.client.request(config);
|
|
129
|
+
return response.data;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
lastError = this.handleError(error);
|
|
132
|
+
if (!this.shouldRetry(error) || attempt === this.maxRetries - 1) {
|
|
133
|
+
throw lastError;
|
|
134
|
+
}
|
|
135
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
136
|
+
await this.sleep(delay);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
throw lastError;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* GET request
|
|
143
|
+
*/
|
|
144
|
+
async get(url, config) {
|
|
145
|
+
return this.request({ ...config, method: "GET", url });
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* POST request
|
|
149
|
+
*/
|
|
150
|
+
async post(url, data, config) {
|
|
151
|
+
return this.request({ ...config, method: "POST", url, data });
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* PUT request
|
|
155
|
+
*/
|
|
156
|
+
async put(url, data, config) {
|
|
157
|
+
return this.request({ ...config, method: "PUT", url, data });
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* PATCH request
|
|
161
|
+
*/
|
|
162
|
+
async patch(url, data, config) {
|
|
163
|
+
return this.request({ ...config, method: "PATCH", url, data });
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* DELETE request
|
|
167
|
+
*/
|
|
168
|
+
async delete(url, config) {
|
|
169
|
+
return this.request({ ...config, method: "DELETE", url });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Handle axios errors and convert to SDK errors
|
|
173
|
+
* Extracts details from backend HttpException format
|
|
174
|
+
*/
|
|
175
|
+
handleError(error) {
|
|
176
|
+
if (!error.response) {
|
|
177
|
+
if (error.code === "ECONNABORTED") {
|
|
178
|
+
return new TimeoutError("Request timeout");
|
|
179
|
+
}
|
|
180
|
+
return new NetworkError(error.message || "Network request failed");
|
|
181
|
+
}
|
|
182
|
+
const { status, data } = error.response;
|
|
183
|
+
const errorData = data;
|
|
184
|
+
const message = errorData?.message || error.message || "An error occurred";
|
|
185
|
+
const details = errorData?.data;
|
|
186
|
+
switch (status) {
|
|
187
|
+
case 401:
|
|
188
|
+
return new AuthenticationError(message);
|
|
189
|
+
case 403:
|
|
190
|
+
return new AuthorizationError(message);
|
|
191
|
+
case 404:
|
|
192
|
+
return new NotFoundError(message);
|
|
193
|
+
case 400:
|
|
194
|
+
return new ValidationError(message, details);
|
|
195
|
+
case 429:
|
|
196
|
+
return new RateLimitError(message);
|
|
197
|
+
case 500:
|
|
198
|
+
case 502:
|
|
199
|
+
case 503:
|
|
200
|
+
case 504:
|
|
201
|
+
return new ServerError(message);
|
|
202
|
+
default:
|
|
203
|
+
return new PromptMetricsError(message, status, void 0, details);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Determine if request should be retried
|
|
208
|
+
*/
|
|
209
|
+
shouldRetry(error) {
|
|
210
|
+
if (!error.response) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
const status = error.response.status;
|
|
214
|
+
return status >= 500 || status === 429;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Sleep utility for retry backoff
|
|
218
|
+
*/
|
|
219
|
+
sleep(ms) {
|
|
220
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/resources/base.ts
|
|
225
|
+
var BaseResource = class {
|
|
226
|
+
constructor(http) {
|
|
227
|
+
this.http = http;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Transform backend response to SDK format
|
|
231
|
+
* Extracts data from the general response wrapper
|
|
232
|
+
*
|
|
233
|
+
* Backend format: { success: true, message: "...", responseData: { data: {...} } }
|
|
234
|
+
* SDK format: Just the data
|
|
235
|
+
*/
|
|
236
|
+
transformResponse(backendResponse) {
|
|
237
|
+
if (typeof backendResponse === "object" && backendResponse !== null && "responseData" in backendResponse && backendResponse.responseData?.data !== void 0) {
|
|
238
|
+
return backendResponse.responseData.data;
|
|
239
|
+
}
|
|
240
|
+
return backendResponse;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// src/resources/templates.ts
|
|
245
|
+
var TemplateResource = class extends BaseResource {
|
|
246
|
+
/**
|
|
247
|
+
* Get a template by ID or name
|
|
248
|
+
* Workspace ID is automatically determined from API key
|
|
249
|
+
*
|
|
250
|
+
* @param identifier - Template ID or name
|
|
251
|
+
* @param options - Optional version and env_label filters
|
|
252
|
+
* @param options.version - Specific version number to fetch
|
|
253
|
+
* @param options.env_label - Environment label: 'development' | 'staging' | 'production'
|
|
254
|
+
* @returns Template with versions
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* // Get template by ID (returns all versions)
|
|
259
|
+
* const template = await client.templates.get('template_id_123');
|
|
260
|
+
*
|
|
261
|
+
* // Get template by name (returns all versions)
|
|
262
|
+
* const template = await client.templates.get('my-template-name');
|
|
263
|
+
*
|
|
264
|
+
* // Get specific version
|
|
265
|
+
* const template = await client.templates.get('my-template', {
|
|
266
|
+
* version: 2
|
|
267
|
+
* });
|
|
268
|
+
*
|
|
269
|
+
* // Get by environment label
|
|
270
|
+
* const template = await client.templates.get('my-template', {
|
|
271
|
+
* env_label: 'production'
|
|
272
|
+
* });
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
async get(identifier, options) {
|
|
276
|
+
const response = await this.http.get(
|
|
277
|
+
`/template/${identifier}`,
|
|
278
|
+
{
|
|
279
|
+
params: options || {}
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
const template = this.transformResponse(response);
|
|
283
|
+
if (!options?.version && !options?.env_label && template.versions) {
|
|
284
|
+
const productionVersion = template.versions.find(
|
|
285
|
+
(v) => v.env_label === "production"
|
|
286
|
+
);
|
|
287
|
+
if (productionVersion) {
|
|
288
|
+
template.versions = [productionVersion];
|
|
289
|
+
} else {
|
|
290
|
+
const latestVersion = template.versions.reduce(
|
|
291
|
+
(latest, current) => current.version > latest.version ? current : latest
|
|
292
|
+
);
|
|
293
|
+
template.versions = [latestVersion];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return template;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* List prompts (templates) with advanced filtering
|
|
300
|
+
* Workspace ID is automatically determined from API key
|
|
301
|
+
*
|
|
302
|
+
* @param options - Filter and pagination options
|
|
303
|
+
* @param options.search - Search in prompt name/description
|
|
304
|
+
* @param options.parent_id - Filter by parent folder ID
|
|
305
|
+
* @param options.risk_level - Filter by risk level: 'LOW' | 'MEDIUM' | 'HIGH'
|
|
306
|
+
* @param options.created_by - Filter by creator user ID
|
|
307
|
+
* @param options.start_date - Filter by creation date (ISO string)
|
|
308
|
+
* @param options.end_date - Filter by creation date (ISO string)
|
|
309
|
+
* @param options.sort_by - Sort field: 'created_at' | 'updated_at' | 'name'
|
|
310
|
+
* @param options.sort_order - Sort order: 'asc' | 'desc'
|
|
311
|
+
* @param options.page - Page number (default: 1)
|
|
312
|
+
* @param options.per_page - Items per page (default: 50)
|
|
313
|
+
* @returns Prompts list with total count
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```typescript
|
|
317
|
+
* // List all prompts
|
|
318
|
+
* const result = await client.templates.list();
|
|
319
|
+
* console.log(`Found ${result.total} prompts`);
|
|
320
|
+
*
|
|
321
|
+
* // Search and filter prompts
|
|
322
|
+
* const result = await client.templates.list({
|
|
323
|
+
* search: 'customer',
|
|
324
|
+
* risk_level: 'HIGH',
|
|
325
|
+
* sort_by: 'updated_at',
|
|
326
|
+
* sort_order: 'desc'
|
|
327
|
+
* });
|
|
328
|
+
*
|
|
329
|
+
* // Paginate results
|
|
330
|
+
* const result = await client.templates.list({
|
|
331
|
+
* page: 1,
|
|
332
|
+
* per_page: 20
|
|
333
|
+
* });
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
async list(options) {
|
|
337
|
+
const response = await this.http.get(
|
|
338
|
+
"/template/prompts",
|
|
339
|
+
{
|
|
340
|
+
params: options || {}
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
return this.transformResponse(response);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Run a template (canary-aware)
|
|
347
|
+
*
|
|
348
|
+
* This method supports canary deployments:
|
|
349
|
+
* - If no version/env_label specified: Uses production with canary support
|
|
350
|
+
* - If specific version/env_label specified: Uses that version directly (no canary)
|
|
351
|
+
*
|
|
352
|
+
* @param identifier - Template ID or name
|
|
353
|
+
* @param options - Run options including variables, model overrides, and version selection
|
|
354
|
+
* @returns PromptLog with execution results
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* // Run template with production version (canary-aware)
|
|
359
|
+
* const result = await client.templates.run('my-template', {
|
|
360
|
+
* variables: { name: 'John', topic: 'AI' }
|
|
361
|
+
* });
|
|
362
|
+
*
|
|
363
|
+
* // Run specific version (bypasses canary)
|
|
364
|
+
* const result = await client.templates.run('my-template', {
|
|
365
|
+
* version: 3,
|
|
366
|
+
* variables: { name: 'John' }
|
|
367
|
+
* });
|
|
368
|
+
*
|
|
369
|
+
* // Run with environment label
|
|
370
|
+
* const result = await client.templates.run('my-template', {
|
|
371
|
+
* env_label: 'staging',
|
|
372
|
+
* variables: { name: 'John' }
|
|
373
|
+
* });
|
|
374
|
+
*
|
|
375
|
+
* // Run with model override and tags
|
|
376
|
+
* const result = await client.templates.run('my-template', {
|
|
377
|
+
* variables: { name: 'John' },
|
|
378
|
+
* model: 'gpt-4',
|
|
379
|
+
* pm_tags: ['production', 'customer-support']
|
|
380
|
+
* });
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
async run(identifier, options) {
|
|
384
|
+
const response = await this.http.post(
|
|
385
|
+
`/template/${identifier}/run`,
|
|
386
|
+
options || {}
|
|
387
|
+
);
|
|
388
|
+
return this.transformResponse(response);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/resources/logs.ts
|
|
393
|
+
var LogResource = class extends BaseResource {
|
|
394
|
+
/**
|
|
395
|
+
* List prompt logs (SDK logs only by default)
|
|
396
|
+
*
|
|
397
|
+
* @param options - List options including filters, pagination
|
|
398
|
+
* @returns Array of prompt logs
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* // List all SDK logs (workspace_id from API key)
|
|
403
|
+
* const logs = await client.logs.list({});
|
|
404
|
+
*
|
|
405
|
+
* // Filter by template
|
|
406
|
+
* const logs = await client.logs.list({
|
|
407
|
+
* template_id: 'template_123'
|
|
408
|
+
* });
|
|
409
|
+
*
|
|
410
|
+
* // Filter by template version
|
|
411
|
+
* const logs = await client.logs.list({
|
|
412
|
+
* template_version_id: 'version_123'
|
|
413
|
+
* });
|
|
414
|
+
*
|
|
415
|
+
* // Filter by date range
|
|
416
|
+
* const logs = await client.logs.list({
|
|
417
|
+
* start_date: '2024-01-01',
|
|
418
|
+
* end_date: '2024-01-31'
|
|
419
|
+
* });
|
|
420
|
+
*
|
|
421
|
+
* // Filter by status
|
|
422
|
+
* const logs = await client.logs.list({
|
|
423
|
+
* status: 'SUCCESS'
|
|
424
|
+
* });
|
|
425
|
+
*
|
|
426
|
+
* // Paginate and sort results
|
|
427
|
+
* const logs = await client.logs.list({
|
|
428
|
+
* page: 1,
|
|
429
|
+
* limit: 50,
|
|
430
|
+
* sort_by: 'created_at',
|
|
431
|
+
* sort_order: 'desc'
|
|
432
|
+
* });
|
|
433
|
+
* ```
|
|
434
|
+
*/
|
|
435
|
+
async list(options = {}) {
|
|
436
|
+
const params = {
|
|
437
|
+
source: "SDK",
|
|
438
|
+
...options
|
|
439
|
+
};
|
|
440
|
+
const response = await this.http.get(
|
|
441
|
+
"/prompt-logs",
|
|
442
|
+
{
|
|
443
|
+
params
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
return this.transformResponse(response);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get a single prompt log by ID
|
|
450
|
+
*
|
|
451
|
+
* @param logId - Log ID
|
|
452
|
+
* @returns Prompt log details
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```typescript
|
|
456
|
+
* const log = await client.logs.get('log_123');
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
async get(logId) {
|
|
460
|
+
const response = await this.http.get(
|
|
461
|
+
`/prompt-logs/${logId}`
|
|
462
|
+
);
|
|
463
|
+
return this.transformResponse(response);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/utils/context.ts
|
|
468
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
469
|
+
var ContextManager = class {
|
|
470
|
+
constructor() {
|
|
471
|
+
this.storage = new AsyncLocalStorage();
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Get current trace context
|
|
475
|
+
*/
|
|
476
|
+
getContext() {
|
|
477
|
+
return this.storage.getStore();
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get current trace ID
|
|
481
|
+
*/
|
|
482
|
+
getCurrentTraceId() {
|
|
483
|
+
return this.getContext()?.trace_id;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get current span ID
|
|
487
|
+
*/
|
|
488
|
+
getCurrentSpanId() {
|
|
489
|
+
return this.getContext()?.span_id;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Get current parent span ID
|
|
493
|
+
*/
|
|
494
|
+
getParentSpanId() {
|
|
495
|
+
return this.getContext()?.parent_span_id;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Get current group ID
|
|
499
|
+
*/
|
|
500
|
+
getGroupId() {
|
|
501
|
+
return this.getContext()?.group_id;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Get current group type
|
|
505
|
+
*/
|
|
506
|
+
getGroupType() {
|
|
507
|
+
return this.getContext()?.group_type;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Get current metadata
|
|
511
|
+
*/
|
|
512
|
+
getMetadata() {
|
|
513
|
+
return this.getContext()?.metadata;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Run function with new context
|
|
517
|
+
*/
|
|
518
|
+
run(context, fn) {
|
|
519
|
+
return this.storage.run(context, fn);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Run async function with new context
|
|
523
|
+
*/
|
|
524
|
+
async runAsync(context, fn) {
|
|
525
|
+
return this.storage.run(context, fn);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Update current context (merge with existing)
|
|
529
|
+
*/
|
|
530
|
+
updateContext(updates) {
|
|
531
|
+
const current = this.getContext();
|
|
532
|
+
if (current) {
|
|
533
|
+
Object.assign(current, updates);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Set group for current context and all child spans
|
|
538
|
+
*/
|
|
539
|
+
updateGroup(group_id, group_type) {
|
|
540
|
+
this.updateContext({ group_id, group_type });
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Add metadata to current context
|
|
544
|
+
*/
|
|
545
|
+
addMetadata(metadata) {
|
|
546
|
+
const current = this.getContext();
|
|
547
|
+
if (current) {
|
|
548
|
+
current.metadata = { ...current.metadata, ...metadata };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Add score to current context
|
|
553
|
+
*/
|
|
554
|
+
addScore(score) {
|
|
555
|
+
const current = this.getContext();
|
|
556
|
+
if (current) {
|
|
557
|
+
if (!current.scores) {
|
|
558
|
+
current.scores = [];
|
|
559
|
+
}
|
|
560
|
+
const scoreWithTimestamp = {
|
|
561
|
+
...score,
|
|
562
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
563
|
+
};
|
|
564
|
+
current.scores.push(scoreWithTimestamp);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
var contextManager = new ContextManager();
|
|
569
|
+
|
|
570
|
+
// src/resources/versions.ts
|
|
571
|
+
var VersionResource = class extends BaseResource {
|
|
572
|
+
async get(identifier, versionNumber) {
|
|
573
|
+
if (versionNumber !== void 0) {
|
|
574
|
+
const response = await this.http.get(
|
|
575
|
+
`/template-version/${identifier}`,
|
|
576
|
+
{ params: { version: versionNumber } }
|
|
577
|
+
);
|
|
578
|
+
return this.transformResponse(response);
|
|
579
|
+
} else {
|
|
580
|
+
const response = await this.http.get(
|
|
581
|
+
`/template-version/${identifier}`
|
|
582
|
+
);
|
|
583
|
+
return this.transformResponse(response);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Run a template version with variables
|
|
588
|
+
*
|
|
589
|
+
* Automatically correlates with active trace context if called within @traceable function.
|
|
590
|
+
* The prompt_log will be linked to the current trace for end-to-end observability.
|
|
591
|
+
*
|
|
592
|
+
* @param versionId - Version ID to run
|
|
593
|
+
* @param options - Run options including variables and parameters
|
|
594
|
+
* @returns Prompt execution log
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* // Run a version with variables
|
|
599
|
+
* const result = await client.versions.run('version_123', {
|
|
600
|
+
* variables: {
|
|
601
|
+
* user_name: 'John',
|
|
602
|
+
* topic: 'AI'
|
|
603
|
+
* }
|
|
604
|
+
* });
|
|
605
|
+
*
|
|
606
|
+
* // Run with custom parameters
|
|
607
|
+
* const result = await client.versions.run('version_123', {
|
|
608
|
+
* variables: { topic: 'ML' },
|
|
609
|
+
* parameters: {
|
|
610
|
+
* temperature: 0.7,
|
|
611
|
+
* max_tokens: 500
|
|
612
|
+
* }
|
|
613
|
+
* });
|
|
614
|
+
*
|
|
615
|
+
* // Run with custom tags (stored directly in prompt log)
|
|
616
|
+
* const result = await client.versions.run('version_123', {
|
|
617
|
+
* variables: { topic: 'AI' },
|
|
618
|
+
* pm_tags: ['customer-support', 'high-priority', 'eu-region']
|
|
619
|
+
* });
|
|
620
|
+
*
|
|
621
|
+
* // Run within traced function (auto-correlation)
|
|
622
|
+
* @pm.traceable({ name: 'generate_response' })
|
|
623
|
+
* async generateResponse(input: string) {
|
|
624
|
+
* // This run will be automatically linked to the trace
|
|
625
|
+
* const result = await pm.versions.run('version_123', {
|
|
626
|
+
* variables: { input }
|
|
627
|
+
* });
|
|
628
|
+
* return result;
|
|
629
|
+
* }
|
|
630
|
+
* ```
|
|
631
|
+
*/
|
|
632
|
+
async run(versionId, options) {
|
|
633
|
+
const context = contextManager.getContext();
|
|
634
|
+
const requestPayload = {
|
|
635
|
+
...options,
|
|
636
|
+
// Include trace correlation data if available
|
|
637
|
+
trace_id: context?.trace_id,
|
|
638
|
+
span_id: context?.span_id,
|
|
639
|
+
group_id: context?.group_id
|
|
640
|
+
};
|
|
641
|
+
const response = await this.http.post(
|
|
642
|
+
`/template-version/${versionId}/run`,
|
|
643
|
+
requestPayload
|
|
644
|
+
);
|
|
645
|
+
return this.transformResponse(response);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Update a template version
|
|
649
|
+
*
|
|
650
|
+
* @param versionId - Version ID to update
|
|
651
|
+
* @param options - Update options (env_label, metadata, etc.)
|
|
652
|
+
* @returns Updated template version
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```typescript
|
|
656
|
+
* // Update environment label
|
|
657
|
+
* const version = await client.versions.update('version_123', {
|
|
658
|
+
* env_label: 'production'
|
|
659
|
+
* });
|
|
660
|
+
*
|
|
661
|
+
* // Update metadata
|
|
662
|
+
* const version = await client.versions.update('version_123', {
|
|
663
|
+
* metadata: {
|
|
664
|
+
* deployed_by: 'John Doe',
|
|
665
|
+
* deployment_date: '2024-01-15'
|
|
666
|
+
* }
|
|
667
|
+
* });
|
|
668
|
+
*
|
|
669
|
+
* // Update both env_label and metadata
|
|
670
|
+
* const version = await client.versions.update('version_123', {
|
|
671
|
+
* env_label: 'staging',
|
|
672
|
+
* metadata: { tested: true },
|
|
673
|
+
* });
|
|
674
|
+
*
|
|
675
|
+
* ```
|
|
676
|
+
*/
|
|
677
|
+
async update(versionId, options) {
|
|
678
|
+
const response = await this.http.put(
|
|
679
|
+
`/template-version/${versionId}`,
|
|
680
|
+
options
|
|
681
|
+
);
|
|
682
|
+
return this.transformResponse(response);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// src/resources/providers.ts
|
|
687
|
+
var ProviderResource = class extends BaseResource {
|
|
688
|
+
/**
|
|
689
|
+
* List all LLM providers
|
|
690
|
+
*
|
|
691
|
+
* Returns providers with `has_key` flag indicating if user has configured API key
|
|
692
|
+
*
|
|
693
|
+
* @returns Array of LLM providers
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```typescript
|
|
697
|
+
* // List all providers
|
|
698
|
+
* const providers = await client.providers.list();
|
|
699
|
+
*
|
|
700
|
+
* // Check which providers have keys configured
|
|
701
|
+
* const configuredProviders = providers.filter(p => p.has_key);
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
async list() {
|
|
705
|
+
const response = await this.http.get(
|
|
706
|
+
"/llm-provider"
|
|
707
|
+
);
|
|
708
|
+
return this.transformResponse(response);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Get models for a specific provider by slug
|
|
712
|
+
*
|
|
713
|
+
* Returns provider details with all available models and parameters.
|
|
714
|
+
* Only returns models if user has API key configured for the provider.
|
|
715
|
+
*
|
|
716
|
+
* @param slug - Provider slug (e.g., 'openai', 'anthropic', 'google')
|
|
717
|
+
* @returns Provider with models and parameters
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```typescript
|
|
721
|
+
* // Get OpenAI models
|
|
722
|
+
* const openai = await client.providers.getModels('openai');
|
|
723
|
+
* console.log(openai.models); // Array of OpenAI models
|
|
724
|
+
*
|
|
725
|
+
* // Get Anthropic models
|
|
726
|
+
* const anthropic = await client.providers.getModels('anthropic');
|
|
727
|
+
* console.log(anthropic.models); // Array of Claude models
|
|
728
|
+
*
|
|
729
|
+
* // Get Google models
|
|
730
|
+
* const google = await client.providers.getModels('google');
|
|
731
|
+
* console.log(google.models); // Array of Gemini models
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
async getModels(slug) {
|
|
735
|
+
const response = await this.http.get(
|
|
736
|
+
`/llm-provider/${slug}/models`
|
|
737
|
+
);
|
|
738
|
+
return this.transformResponse(response);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
// src/resources/traces.ts
|
|
743
|
+
var TraceResource = class extends BaseResource {
|
|
744
|
+
/**
|
|
745
|
+
* Create a new trace
|
|
746
|
+
*
|
|
747
|
+
* @param options - Trace creation options
|
|
748
|
+
* @returns Created trace
|
|
749
|
+
*
|
|
750
|
+
* @example
|
|
751
|
+
* ```typescript
|
|
752
|
+
* const trace = await client.traces.create({
|
|
753
|
+
* trace_id: 'trace_123',
|
|
754
|
+
* span_id: 'span_abc',
|
|
755
|
+
* function_name: 'process_customer_data',
|
|
756
|
+
* function_type: 'CUSTOM',
|
|
757
|
+
* start_time: new Date('2025-12-08T10:00:00Z'),
|
|
758
|
+
* end_time: new Date('2025-12-08T10:00:02Z'),
|
|
759
|
+
* duration_ms: 2000,
|
|
760
|
+
* status: 'SUCCESS',
|
|
761
|
+
* metadata: {
|
|
762
|
+
* customer_id: '12345',
|
|
763
|
+
* processing_stage: 'enrichment'
|
|
764
|
+
* }
|
|
765
|
+
* });
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
async create(options) {
|
|
769
|
+
const response = await this.http.post(
|
|
770
|
+
"/traces",
|
|
771
|
+
options
|
|
772
|
+
);
|
|
773
|
+
return this.transformResponse(response);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Create multiple traces in batch (optimized for high throughput)
|
|
777
|
+
*
|
|
778
|
+
* Reduces HTTP overhead by sending multiple traces in a single request.
|
|
779
|
+
* Supports partial success - some traces may succeed while others fail.
|
|
780
|
+
*
|
|
781
|
+
* @param traces - Array of trace creation options (max 100)
|
|
782
|
+
* @returns Batch result with created traces and errors
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```typescript
|
|
786
|
+
* const result = await client.traces.createBatch([
|
|
787
|
+
* {
|
|
788
|
+
* trace_id: 'trace_123',
|
|
789
|
+
* span_id: 'span_abc',
|
|
790
|
+
* function_name: 'step_1',
|
|
791
|
+
* start_time: new Date(),
|
|
792
|
+
* end_time: new Date(),
|
|
793
|
+
* duration_ms: 100,
|
|
794
|
+
* status: 'SUCCESS'
|
|
795
|
+
* },
|
|
796
|
+
* {
|
|
797
|
+
* trace_id: 'trace_123',
|
|
798
|
+
* span_id: 'span_def',
|
|
799
|
+
* function_name: 'step_2',
|
|
800
|
+
* start_time: new Date(),
|
|
801
|
+
* end_time: new Date(),
|
|
802
|
+
* duration_ms: 200,
|
|
803
|
+
* status: 'SUCCESS'
|
|
804
|
+
* }
|
|
805
|
+
* ]);
|
|
806
|
+
*
|
|
807
|
+
* console.log(`Created: ${result.summary.successful}, Failed: ${result.summary.failed}`);
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
async createBatch(traces) {
|
|
811
|
+
if (!traces || traces.length === 0) {
|
|
812
|
+
throw new Error("Traces array cannot be empty");
|
|
813
|
+
}
|
|
814
|
+
if (traces.length > 100) {
|
|
815
|
+
throw new Error("Maximum 100 traces allowed per batch");
|
|
816
|
+
}
|
|
817
|
+
const response = await this.http.post("/traces/batch", { traces });
|
|
818
|
+
return this.transformResponse(response);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Add a score to a trace
|
|
822
|
+
*
|
|
823
|
+
* @param span_id - Span ID of the trace
|
|
824
|
+
* @param options - Score options
|
|
825
|
+
* @returns Updated trace with score
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* ```typescript
|
|
829
|
+
* const trace = await client.traces.addScore('span_abc', {
|
|
830
|
+
* criteria: 'coherence',
|
|
831
|
+
* value: 0.92
|
|
832
|
+
* });
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
async addScore(span_id, options) {
|
|
836
|
+
const response = await this.http.post(
|
|
837
|
+
`/traces/${span_id}/score`,
|
|
838
|
+
options
|
|
839
|
+
);
|
|
840
|
+
return this.transformResponse(response);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Update trace metadata
|
|
844
|
+
*
|
|
845
|
+
* @param span_id - Span ID of the trace
|
|
846
|
+
* @param options - Metadata update options
|
|
847
|
+
* @returns Updated trace
|
|
848
|
+
*
|
|
849
|
+
* @example
|
|
850
|
+
* ```typescript
|
|
851
|
+
* const trace = await client.traces.updateMetadata('span_abc', {
|
|
852
|
+
* metadata: {
|
|
853
|
+
* processing_result: 'completed',
|
|
854
|
+
* items_processed: 150
|
|
855
|
+
* }
|
|
856
|
+
* });
|
|
857
|
+
* ```
|
|
858
|
+
*/
|
|
859
|
+
async updateMetadata(span_id, options) {
|
|
860
|
+
const response = await this.http.patch(
|
|
861
|
+
`/traces/${span_id}/metadata`,
|
|
862
|
+
options
|
|
863
|
+
);
|
|
864
|
+
return this.transformResponse(response);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Get a single trace by span_id
|
|
868
|
+
*
|
|
869
|
+
* @param span_id - Span ID of the trace
|
|
870
|
+
* @returns Trace details
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```typescript
|
|
874
|
+
* const trace = await client.traces.getBySpanId('span_abc');
|
|
875
|
+
* console.log(trace.function_name, trace.duration_ms);
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
878
|
+
async getBySpanId(span_id) {
|
|
879
|
+
const response = await this.http.get(
|
|
880
|
+
`/traces/span/${span_id}`
|
|
881
|
+
);
|
|
882
|
+
return this.transformResponse(response);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Get entire trace tree by trace_id
|
|
886
|
+
*
|
|
887
|
+
* Returns all spans in the trace organized as a tree structure
|
|
888
|
+
* with parent-child relationships
|
|
889
|
+
*
|
|
890
|
+
* @param trace_id - Trace ID
|
|
891
|
+
* @returns Array of trace tree nodes with nested children
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```typescript
|
|
895
|
+
* const tree = await client.traces.getTrace('trace_123');
|
|
896
|
+
*
|
|
897
|
+
* // Tree structure with children
|
|
898
|
+
* tree.forEach(root => {
|
|
899
|
+
* console.log(`Root: ${root.function_name}`);
|
|
900
|
+
* root.children.forEach(child => {
|
|
901
|
+
* console.log(` Child: ${child.function_name}`);
|
|
902
|
+
* });
|
|
903
|
+
* });
|
|
904
|
+
* ```
|
|
905
|
+
*/
|
|
906
|
+
async getTrace(trace_id) {
|
|
907
|
+
const response = await this.http.get(
|
|
908
|
+
`/traces/trace/${trace_id}`
|
|
909
|
+
);
|
|
910
|
+
return this.transformResponse(response);
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Get all traces for a group_id
|
|
914
|
+
*
|
|
915
|
+
* @param group_id - Group ID (e.g., conversation_id, workflow_id)
|
|
916
|
+
* @returns Array of traces in the group
|
|
917
|
+
*
|
|
918
|
+
* @example
|
|
919
|
+
* ```typescript
|
|
920
|
+
* const traces = await client.traces.getGroup('conversation_abc');
|
|
921
|
+
* console.log(`Found ${traces.length} traces in conversation`);
|
|
922
|
+
* ```
|
|
923
|
+
*/
|
|
924
|
+
async getGroup(group_id) {
|
|
925
|
+
const response = await this.http.get(
|
|
926
|
+
`/traces/group/${group_id}`
|
|
927
|
+
);
|
|
928
|
+
return this.transformResponse(response);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* List traces with filters and pagination
|
|
932
|
+
*
|
|
933
|
+
* @param options - List options with filters
|
|
934
|
+
* @returns Paginated list of traces
|
|
935
|
+
*
|
|
936
|
+
* @example
|
|
937
|
+
* ```typescript
|
|
938
|
+
* // List all traces
|
|
939
|
+
* const result = await client.traces.list();
|
|
940
|
+
*
|
|
941
|
+
* // Filter by function name
|
|
942
|
+
* const filtered = await client.traces.list({
|
|
943
|
+
* function_name: 'process_customer_data',
|
|
944
|
+
* status: 'SUCCESS',
|
|
945
|
+
* page: 1,
|
|
946
|
+
* limit: 50
|
|
947
|
+
* });
|
|
948
|
+
*
|
|
949
|
+
* // Filter by date range
|
|
950
|
+
* const recent = await client.traces.list({
|
|
951
|
+
* start_date: '2025-12-01T00:00:00Z',
|
|
952
|
+
* end_date: '2025-12-08T23:59:59Z'
|
|
953
|
+
* });
|
|
954
|
+
* ```
|
|
955
|
+
*/
|
|
956
|
+
async list(options) {
|
|
957
|
+
const response = await this.http.get(
|
|
958
|
+
"/traces",
|
|
959
|
+
{ params: options }
|
|
960
|
+
);
|
|
961
|
+
return this.transformResponse(response);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Get trace analytics for workspace
|
|
965
|
+
*
|
|
966
|
+
* Returns aggregated statistics about trace execution including
|
|
967
|
+
* success rates, average duration, and function-level breakdown
|
|
968
|
+
*
|
|
969
|
+
* @param options - Optional date range filter
|
|
970
|
+
* @returns Analytics data
|
|
971
|
+
*
|
|
972
|
+
* @example
|
|
973
|
+
* ```typescript
|
|
974
|
+
* // Get all-time analytics
|
|
975
|
+
* const analytics = await client.traces.getAnalytics();
|
|
976
|
+
* console.log(`Success rate: ${analytics.success_rate}%`);
|
|
977
|
+
* console.log(`Avg duration: ${analytics.average_duration_ms}ms`);
|
|
978
|
+
*
|
|
979
|
+
* // Get analytics for date range
|
|
980
|
+
* const weeklyAnalytics = await client.traces.getAnalytics({
|
|
981
|
+
* start_date: '2025-12-01T00:00:00Z',
|
|
982
|
+
* end_date: '2025-12-08T23:59:59Z'
|
|
983
|
+
* });
|
|
984
|
+
*
|
|
985
|
+
* // Function breakdown
|
|
986
|
+
* analytics.function_breakdown.forEach(fn => {
|
|
987
|
+
* console.log(`${fn.function_name}: ${fn.count} calls, ${fn.avg_duration_ms}ms avg`);
|
|
988
|
+
* });
|
|
989
|
+
* ```
|
|
990
|
+
*/
|
|
991
|
+
async getAnalytics(options) {
|
|
992
|
+
const response = await this.http.get(
|
|
993
|
+
"/traces/analytics",
|
|
994
|
+
{ params: options }
|
|
995
|
+
);
|
|
996
|
+
return this.transformResponse(response);
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Delete old traces (cleanup)
|
|
1000
|
+
*
|
|
1001
|
+
* Deletes traces older than the specified date
|
|
1002
|
+
*
|
|
1003
|
+
* @param before_date - Delete traces before this date
|
|
1004
|
+
* @returns Number of traces deleted
|
|
1005
|
+
*
|
|
1006
|
+
* @example
|
|
1007
|
+
* ```typescript
|
|
1008
|
+
* // Delete traces older than 90 days
|
|
1009
|
+
* const ninetyDaysAgo = new Date();
|
|
1010
|
+
* ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
|
1011
|
+
*
|
|
1012
|
+
* const result = await client.traces.cleanup(ninetyDaysAgo.toISOString());
|
|
1013
|
+
* console.log(`Deleted ${result.deleted_count} old traces`);
|
|
1014
|
+
* ```
|
|
1015
|
+
*/
|
|
1016
|
+
async cleanup(before_date) {
|
|
1017
|
+
const response = await this.http.delete("/traces/cleanup", {
|
|
1018
|
+
data: { before_date }
|
|
1019
|
+
});
|
|
1020
|
+
return this.transformResponse(response);
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/utils/track.ts
|
|
1025
|
+
var Track = class {
|
|
1026
|
+
/**
|
|
1027
|
+
* Add metadata to the current span
|
|
1028
|
+
* Metadata is collected in context and sent when the trace is created
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```typescript
|
|
1032
|
+
* pm.track.metadata({
|
|
1033
|
+
* customer_id: '12345',
|
|
1034
|
+
* processing_stage: 'enrichment'
|
|
1035
|
+
* });
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
metadata(extraData) {
|
|
1039
|
+
const spanId = contextManager.getCurrentSpanId();
|
|
1040
|
+
if (!spanId) {
|
|
1041
|
+
throw new Error(
|
|
1042
|
+
"No active span context. metadata() must be called within a @traceable function."
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
contextManager.addMetadata(extraData);
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Add a score to the current span
|
|
1049
|
+
* Score is collected in context and sent when the trace is created
|
|
1050
|
+
*
|
|
1051
|
+
* @example
|
|
1052
|
+
* ```typescript
|
|
1053
|
+
* pm.track.score({
|
|
1054
|
+
* criteria: 'coherence',
|
|
1055
|
+
* value: 0.92
|
|
1056
|
+
* });
|
|
1057
|
+
* ```
|
|
1058
|
+
*/
|
|
1059
|
+
score(options) {
|
|
1060
|
+
const spanId = contextManager.getCurrentSpanId();
|
|
1061
|
+
if (!spanId) {
|
|
1062
|
+
throw new Error(
|
|
1063
|
+
"No active span context. score() must be called within a @traceable function."
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
contextManager.addScore(options);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Set group for current context and all child spans
|
|
1070
|
+
* Uses context to propagate group to nested functions
|
|
1071
|
+
*
|
|
1072
|
+
* @example
|
|
1073
|
+
* ```typescript
|
|
1074
|
+
* pm.track.group({
|
|
1075
|
+
* group_id: 'conversation_abc',
|
|
1076
|
+
* group_type: 'conversation' // Flexible - use any string
|
|
1077
|
+
* });
|
|
1078
|
+
* ```
|
|
1079
|
+
*/
|
|
1080
|
+
group(options) {
|
|
1081
|
+
contextManager.updateGroup(options.group_id, options.group_type);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
|
|
1085
|
+
// src/utils/id-generator.ts
|
|
1086
|
+
import { randomBytes } from "crypto";
|
|
1087
|
+
var IdGenerator = class {
|
|
1088
|
+
/**
|
|
1089
|
+
* Generate a unique trace ID
|
|
1090
|
+
*/
|
|
1091
|
+
static generateTraceId() {
|
|
1092
|
+
return `trace_${Date.now()}_${randomBytes(8).toString("hex")}`;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Generate a unique span ID
|
|
1096
|
+
*/
|
|
1097
|
+
static generateSpanId() {
|
|
1098
|
+
return `span_${Date.now()}_${randomBytes(8).toString("hex")}`;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Generate a unique group ID
|
|
1102
|
+
*/
|
|
1103
|
+
static generateGroupId(prefix = "group") {
|
|
1104
|
+
return `${prefix}_${Date.now()}_${randomBytes(8).toString("hex")}`;
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
// src/decorators/traceable.ts
|
|
1109
|
+
function traceable(traceResource, options = {}) {
|
|
1110
|
+
return function(_target, propertyKey, descriptor) {
|
|
1111
|
+
const originalMethod = descriptor.value;
|
|
1112
|
+
if (options.disabled) {
|
|
1113
|
+
return descriptor;
|
|
1114
|
+
}
|
|
1115
|
+
descriptor.value = async function(...args) {
|
|
1116
|
+
const functionName = options.name || propertyKey;
|
|
1117
|
+
const currentContext = contextManager.getContext();
|
|
1118
|
+
const trace_id = currentContext?.trace_id || IdGenerator.generateTraceId();
|
|
1119
|
+
const span_id = IdGenerator.generateSpanId();
|
|
1120
|
+
const parent_span_id = currentContext?.span_id;
|
|
1121
|
+
const group_id = currentContext?.group_id;
|
|
1122
|
+
const group_type = currentContext?.group_type;
|
|
1123
|
+
const tags = [...currentContext?.tags || [], ...options.tags || []];
|
|
1124
|
+
const metadata = {
|
|
1125
|
+
...currentContext?.metadata,
|
|
1126
|
+
...options.metadata
|
|
1127
|
+
};
|
|
1128
|
+
const start_time = /* @__PURE__ */ new Date();
|
|
1129
|
+
let status = "SUCCESS";
|
|
1130
|
+
let error;
|
|
1131
|
+
let result;
|
|
1132
|
+
let input;
|
|
1133
|
+
let output;
|
|
1134
|
+
let capturedContext;
|
|
1135
|
+
if (args.length > 0) {
|
|
1136
|
+
input = {};
|
|
1137
|
+
args.slice(0, 3).forEach((arg, index) => {
|
|
1138
|
+
try {
|
|
1139
|
+
input[`arg_${index}`] = JSON.parse(JSON.stringify(arg));
|
|
1140
|
+
} catch {
|
|
1141
|
+
input[`arg_${index}`] = String(arg);
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
try {
|
|
1146
|
+
result = await contextManager.runAsync(
|
|
1147
|
+
{
|
|
1148
|
+
trace_id,
|
|
1149
|
+
span_id,
|
|
1150
|
+
parent_span_id,
|
|
1151
|
+
group_id,
|
|
1152
|
+
group_type,
|
|
1153
|
+
tags,
|
|
1154
|
+
metadata,
|
|
1155
|
+
scores: []
|
|
1156
|
+
// Initialize empty scores array
|
|
1157
|
+
},
|
|
1158
|
+
async () => {
|
|
1159
|
+
const res = await originalMethod.apply(this, args);
|
|
1160
|
+
capturedContext = contextManager.getContext();
|
|
1161
|
+
return res;
|
|
1162
|
+
}
|
|
1163
|
+
);
|
|
1164
|
+
try {
|
|
1165
|
+
const serialized = JSON.parse(JSON.stringify(result));
|
|
1166
|
+
if (Array.isArray(serialized)) {
|
|
1167
|
+
output = { result: serialized };
|
|
1168
|
+
} else if (typeof serialized === "object" && serialized !== null) {
|
|
1169
|
+
output = serialized;
|
|
1170
|
+
} else {
|
|
1171
|
+
output = { result: serialized };
|
|
1172
|
+
}
|
|
1173
|
+
} catch {
|
|
1174
|
+
output = { result: String(result) };
|
|
1175
|
+
}
|
|
1176
|
+
} catch (err) {
|
|
1177
|
+
status = "ERROR";
|
|
1178
|
+
error = {
|
|
1179
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1180
|
+
stack: err instanceof Error ? err.stack : void 0,
|
|
1181
|
+
code: err.code
|
|
1182
|
+
};
|
|
1183
|
+
throw err;
|
|
1184
|
+
} finally {
|
|
1185
|
+
const end_time = /* @__PURE__ */ new Date();
|
|
1186
|
+
const duration_ms = end_time.getTime() - start_time.getTime();
|
|
1187
|
+
try {
|
|
1188
|
+
const contextScores = capturedContext?.scores;
|
|
1189
|
+
const contextMetadata = capturedContext?.metadata || {};
|
|
1190
|
+
const finalMetadata = {
|
|
1191
|
+
...metadata,
|
|
1192
|
+
...contextMetadata
|
|
1193
|
+
};
|
|
1194
|
+
await traceResource.create({
|
|
1195
|
+
trace_id,
|
|
1196
|
+
span_id,
|
|
1197
|
+
parent_span_id,
|
|
1198
|
+
function_name: functionName,
|
|
1199
|
+
start_time,
|
|
1200
|
+
end_time,
|
|
1201
|
+
duration_ms,
|
|
1202
|
+
status,
|
|
1203
|
+
error,
|
|
1204
|
+
input,
|
|
1205
|
+
output,
|
|
1206
|
+
metadata: finalMetadata,
|
|
1207
|
+
scores: contextScores,
|
|
1208
|
+
tags,
|
|
1209
|
+
group_id,
|
|
1210
|
+
group_type
|
|
1211
|
+
});
|
|
1212
|
+
} catch (traceError) {
|
|
1213
|
+
console.error("[PromptMetrics] Failed to send trace:", traceError);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return result;
|
|
1217
|
+
};
|
|
1218
|
+
return descriptor;
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
function createTraceableDecorator(traceResource) {
|
|
1222
|
+
return (options = {}) => traceable(traceResource, options);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/client.ts
|
|
1226
|
+
var PromptMetrics = class {
|
|
1227
|
+
/**
|
|
1228
|
+
* Create a new PromptMetrics client
|
|
1229
|
+
*
|
|
1230
|
+
* @param config - Configuration options
|
|
1231
|
+
* @throws {ValidationError} If API key is missing or invalid
|
|
1232
|
+
*/
|
|
1233
|
+
constructor(config) {
|
|
1234
|
+
this.validateConfig(config);
|
|
1235
|
+
const baseURL = process.env.BASE_URL;
|
|
1236
|
+
if (!baseURL) {
|
|
1237
|
+
throw new ValidationError(
|
|
1238
|
+
"BASE_URL environment variable is required. Please set it in your .env file."
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
this.httpClient = new HttpClient({
|
|
1242
|
+
baseURL,
|
|
1243
|
+
apiKey: config.apiKey,
|
|
1244
|
+
timeout: config.timeout,
|
|
1245
|
+
maxRetries: config.maxRetries,
|
|
1246
|
+
debug: config.debug
|
|
1247
|
+
});
|
|
1248
|
+
this.templates = new TemplateResource(this.httpClient);
|
|
1249
|
+
this.versions = new VersionResource(this.httpClient);
|
|
1250
|
+
this.logs = new LogResource(this.httpClient);
|
|
1251
|
+
this.providers = new ProviderResource(this.httpClient);
|
|
1252
|
+
this.traces = new TraceResource(this.httpClient);
|
|
1253
|
+
this.track = new Track();
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Create a @traceable decorator for automatic function tracking
|
|
1257
|
+
*
|
|
1258
|
+
* @param options - Decorator options
|
|
1259
|
+
* @returns Decorator function
|
|
1260
|
+
*
|
|
1261
|
+
* @example
|
|
1262
|
+
* ```typescript
|
|
1263
|
+
* class MyService {
|
|
1264
|
+
* @pm.traceable({ name: 'process_data' })
|
|
1265
|
+
* async processData(input: string) {
|
|
1266
|
+
* // Automatically tracked
|
|
1267
|
+
* return result;
|
|
1268
|
+
* }
|
|
1269
|
+
* }
|
|
1270
|
+
* ```
|
|
1271
|
+
*/
|
|
1272
|
+
traceable(options = {}) {
|
|
1273
|
+
return createTraceableDecorator(this.traces)(options);
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Get current trace ID from context
|
|
1277
|
+
*/
|
|
1278
|
+
getCurrentTraceId() {
|
|
1279
|
+
return contextManager.getCurrentTraceId();
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Get current span ID from context
|
|
1283
|
+
*/
|
|
1284
|
+
getCurrentSpanId() {
|
|
1285
|
+
return contextManager.getCurrentSpanId();
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Validate configuration
|
|
1289
|
+
*/
|
|
1290
|
+
validateConfig(config) {
|
|
1291
|
+
if (!config.apiKey) {
|
|
1292
|
+
throw new ValidationError(
|
|
1293
|
+
"API key is required. Please provide a valid workspace API key (starts with pm_)."
|
|
1294
|
+
);
|
|
1295
|
+
}
|
|
1296
|
+
if (typeof config.apiKey !== "string") {
|
|
1297
|
+
throw new ValidationError("API key must be a string.");
|
|
1298
|
+
}
|
|
1299
|
+
if (!config.apiKey.startsWith("pm_")) {
|
|
1300
|
+
throw new ValidationError(
|
|
1301
|
+
'Invalid API key format. Workspace API keys should start with "pm_".'
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (config.apiKey.length < 20) {
|
|
1305
|
+
throw new ValidationError(
|
|
1306
|
+
"API key appears to be too short. Please check your API key."
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
// src/index.ts
|
|
1313
|
+
var VERSION = "1.0.0";
|
|
1314
|
+
export {
|
|
1315
|
+
AuthenticationError,
|
|
1316
|
+
AuthorizationError,
|
|
1317
|
+
NetworkError,
|
|
1318
|
+
NotFoundError,
|
|
1319
|
+
PromptMetrics,
|
|
1320
|
+
PromptMetricsError,
|
|
1321
|
+
RateLimitError,
|
|
1322
|
+
ServerError,
|
|
1323
|
+
TimeoutError,
|
|
1324
|
+
VERSION,
|
|
1325
|
+
ValidationError
|
|
1326
|
+
};
|