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