@highflame/sdk 0.3.3 → 0.3.5

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.cjs CHANGED
@@ -148,8 +148,53 @@ async function* streamSseResponse(response) {
148
148
  }
149
149
  }
150
150
 
151
+ // src/telemetry.ts
152
+ var _otelApi;
153
+ function getOtelApi() {
154
+ if (_otelApi !== void 0) return _otelApi;
155
+ try {
156
+ _otelApi = require("@opentelemetry/api");
157
+ } catch {
158
+ _otelApi = null;
159
+ }
160
+ return _otelApi;
161
+ }
162
+ function injectTraceContext(headers) {
163
+ const api = getOtelApi();
164
+ if (!api) return;
165
+ const propagator = api.propagation;
166
+ const context = api.context.active();
167
+ propagator.inject(context, headers, {
168
+ set(carrier, key, value) {
169
+ carrier[key] = value;
170
+ }
171
+ });
172
+ }
173
+ async function withSpan(name, attributes, fn) {
174
+ const api = getOtelApi();
175
+ if (!api) return fn();
176
+ const tracer = api.trace.getTracer("@highflame/sdk", VERSION);
177
+ const parentContext = api.context.active();
178
+ const span = tracer.startSpan(name, { attributes }, parentContext);
179
+ const spanContext = api.trace.setSpan(parentContext, span);
180
+ try {
181
+ const result = await api.context.with(spanContext, fn);
182
+ span.setStatus({ code: api.SpanStatusCode.OK });
183
+ return result;
184
+ } catch (err) {
185
+ span.setStatus({
186
+ code: api.SpanStatusCode.ERROR,
187
+ message: err instanceof Error ? err.message : String(err)
188
+ });
189
+ span.recordException(err instanceof Error ? err : new Error(String(err)));
190
+ throw err;
191
+ } finally {
192
+ span.end();
193
+ }
194
+ }
195
+
151
196
  // src/client.ts
152
- var VERSION = "0.2.0";
197
+ var VERSION = "0.3.4";
153
198
  var DEFAULT_TIMEOUT_MS = 3e4;
154
199
  var DEFAULT_MAX_RETRIES = 2;
155
200
  var RETRY_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
@@ -201,7 +246,14 @@ var GuardResource = class {
201
246
  }
202
247
  /** Evaluate content against guard policies (POST /v1/guard). */
203
248
  async evaluate(request, options) {
204
- return this._client._postJSON("/v1/guard", request, options?.timeout);
249
+ return withSpan(
250
+ "highflame.guard",
251
+ {
252
+ "highflame.action": request.action ?? "process_prompt",
253
+ "highflame.content_type": request.content_type ?? "prompt"
254
+ },
255
+ () => this._client._postJSON("/v1/guard", request, options?.timeout)
256
+ );
205
257
  }
206
258
  /** Shorthand: evaluate a user prompt. */
207
259
  async evaluatePrompt(content, options) {
@@ -236,7 +288,11 @@ var DetectResource = class {
236
288
  }
237
289
  /** Run detectors without policy evaluation (POST /v1/detect). */
238
290
  async run(request, options) {
239
- return this._client._postJSON("/v1/detect", request, options?.timeout);
291
+ return withSpan(
292
+ "highflame.detect",
293
+ { "highflame.content_type": request.content_type ?? "prompt" },
294
+ () => this._client._postJSON("/v1/detect", request, options?.timeout)
295
+ );
240
296
  }
241
297
  };
242
298
  var DetectorsResource = class {
@@ -281,7 +337,7 @@ var Highflame = class {
281
337
  const baseUrl = options.baseUrl ?? SAAS_BASE_URL;
282
338
  const tokenUrl = options.tokenUrl ?? SAAS_TOKEN_URL;
283
339
  this.#baseUrl = baseUrl.replace(/\/$/, "");
284
- this.#tokenUrl = options.apiKey.startsWith("hf_sk") ? tokenUrl : void 0;
340
+ this.#tokenUrl = options.apiKey.startsWith("hf_sk") || options.apiKey.startsWith("hfa_") ? tokenUrl : void 0;
285
341
  this.#apiKey = options.apiKey;
286
342
  this.#timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
287
343
  this.#maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
@@ -407,6 +463,7 @@ var Highflame = class {
407
463
  this.#log.debug("Request body: %s", truncate(init.body));
408
464
  }
409
465
  const authHeaders = await this.getAuthHeaders();
466
+ injectTraceContext(authHeaders);
410
467
  if (idempotencyKey) {
411
468
  authHeaders["X-Idempotency-Key"] = idempotencyKey;
412
469
  }
@@ -468,6 +525,7 @@ var Highflame = class {
468
525
  const effectiveTimeout = overrideTimeout ?? this.#timeout;
469
526
  this.#log.debug("POST %s (stream)", path);
470
527
  const authHeaders = await this.getAuthHeaders();
528
+ injectTraceContext(authHeaders);
471
529
  const controller = new AbortController();
472
530
  const timer = setTimeout(() => controller.abort(), effectiveTimeout);
473
531
  let response;
package/dist/index.d.cts CHANGED
@@ -506,8 +506,8 @@ interface ProblemDetails {
506
506
  interface StreamEvent {
507
507
  /** Event payload (DetectorResult for detector_result, GuardResponse for decision). */
508
508
  data: Record<string, unknown>;
509
- /** Event type: detector_result (per-detector), decision (final), or error. */
510
- type: "detector_result" | "decision" | "error";
509
+ /** Event type: detector_result (per-detector), decision (final), error, or message (keepalive/info). */
510
+ type: "detector_result" | "decision" | "error" | "message";
511
511
  }
512
512
  /** Response from the token exchange endpoint (used by SDK auth). */
513
513
  interface TokenResponse {
@@ -553,7 +553,7 @@ interface TokenResponse {
553
553
  */
554
554
 
555
555
  /** SDK version — kept in sync with package.json via `make version-sync`. */
556
- declare const VERSION = "0.2.0";
556
+ declare const VERSION = "0.3.4";
557
557
  /**
558
558
  * Pluggable logger interface.
559
559
  *
@@ -568,11 +568,11 @@ interface Logger {
568
568
  warn(message: string, ...args: unknown[]): void;
569
569
  }
570
570
  interface HighflameOptions {
571
- /** Service API key (`hf_sk_...`) or a raw JWT. */
571
+ /** Service API key (`hf_sk_...`), agent API key (`hfa_...`), or a raw JWT. */
572
572
  apiKey: string;
573
573
  /** Base URL of the guard service. Defaults to Highflame SaaS. */
574
574
  baseUrl?: string;
575
- /** Token exchange URL. Defaults to Highflame SaaS. Only used for `hf_sk_*` keys. */
575
+ /** Token exchange URL. Defaults to Highflame SaaS. Used for `hf_sk_*` and `hfa_*` keys. */
576
576
  tokenUrl?: string;
577
577
  /** Request timeout in milliseconds. Default: 30000. */
578
578
  timeout?: number;
package/dist/index.d.ts CHANGED
@@ -506,8 +506,8 @@ interface ProblemDetails {
506
506
  interface StreamEvent {
507
507
  /** Event payload (DetectorResult for detector_result, GuardResponse for decision). */
508
508
  data: Record<string, unknown>;
509
- /** Event type: detector_result (per-detector), decision (final), or error. */
510
- type: "detector_result" | "decision" | "error";
509
+ /** Event type: detector_result (per-detector), decision (final), error, or message (keepalive/info). */
510
+ type: "detector_result" | "decision" | "error" | "message";
511
511
  }
512
512
  /** Response from the token exchange endpoint (used by SDK auth). */
513
513
  interface TokenResponse {
@@ -553,7 +553,7 @@ interface TokenResponse {
553
553
  */
554
554
 
555
555
  /** SDK version — kept in sync with package.json via `make version-sync`. */
556
- declare const VERSION = "0.2.0";
556
+ declare const VERSION = "0.3.4";
557
557
  /**
558
558
  * Pluggable logger interface.
559
559
  *
@@ -568,11 +568,11 @@ interface Logger {
568
568
  warn(message: string, ...args: unknown[]): void;
569
569
  }
570
570
  interface HighflameOptions {
571
- /** Service API key (`hf_sk_...`) or a raw JWT. */
571
+ /** Service API key (`hf_sk_...`), agent API key (`hfa_...`), or a raw JWT. */
572
572
  apiKey: string;
573
573
  /** Base URL of the guard service. Defaults to Highflame SaaS. */
574
574
  baseUrl?: string;
575
- /** Token exchange URL. Defaults to Highflame SaaS. Only used for `hf_sk_*` keys. */
575
+ /** Token exchange URL. Defaults to Highflame SaaS. Used for `hf_sk_*` and `hfa_*` keys. */
576
576
  tokenUrl?: string;
577
577
  /** Request timeout in milliseconds. Default: 30000. */
578
578
  timeout?: number;
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/errors.ts
2
9
  var HighflameError = class extends Error {
3
10
  constructor(message) {
@@ -110,8 +117,53 @@ async function* streamSseResponse(response) {
110
117
  }
111
118
  }
112
119
 
120
+ // src/telemetry.ts
121
+ var _otelApi;
122
+ function getOtelApi() {
123
+ if (_otelApi !== void 0) return _otelApi;
124
+ try {
125
+ _otelApi = __require("@opentelemetry/api");
126
+ } catch {
127
+ _otelApi = null;
128
+ }
129
+ return _otelApi;
130
+ }
131
+ function injectTraceContext(headers) {
132
+ const api = getOtelApi();
133
+ if (!api) return;
134
+ const propagator = api.propagation;
135
+ const context = api.context.active();
136
+ propagator.inject(context, headers, {
137
+ set(carrier, key, value) {
138
+ carrier[key] = value;
139
+ }
140
+ });
141
+ }
142
+ async function withSpan(name, attributes, fn) {
143
+ const api = getOtelApi();
144
+ if (!api) return fn();
145
+ const tracer = api.trace.getTracer("@highflame/sdk", VERSION);
146
+ const parentContext = api.context.active();
147
+ const span = tracer.startSpan(name, { attributes }, parentContext);
148
+ const spanContext = api.trace.setSpan(parentContext, span);
149
+ try {
150
+ const result = await api.context.with(spanContext, fn);
151
+ span.setStatus({ code: api.SpanStatusCode.OK });
152
+ return result;
153
+ } catch (err) {
154
+ span.setStatus({
155
+ code: api.SpanStatusCode.ERROR,
156
+ message: err instanceof Error ? err.message : String(err)
157
+ });
158
+ span.recordException(err instanceof Error ? err : new Error(String(err)));
159
+ throw err;
160
+ } finally {
161
+ span.end();
162
+ }
163
+ }
164
+
113
165
  // src/client.ts
114
- var VERSION = "0.2.0";
166
+ var VERSION = "0.3.4";
115
167
  var DEFAULT_TIMEOUT_MS = 3e4;
116
168
  var DEFAULT_MAX_RETRIES = 2;
117
169
  var RETRY_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
@@ -163,7 +215,14 @@ var GuardResource = class {
163
215
  }
164
216
  /** Evaluate content against guard policies (POST /v1/guard). */
165
217
  async evaluate(request, options) {
166
- return this._client._postJSON("/v1/guard", request, options?.timeout);
218
+ return withSpan(
219
+ "highflame.guard",
220
+ {
221
+ "highflame.action": request.action ?? "process_prompt",
222
+ "highflame.content_type": request.content_type ?? "prompt"
223
+ },
224
+ () => this._client._postJSON("/v1/guard", request, options?.timeout)
225
+ );
167
226
  }
168
227
  /** Shorthand: evaluate a user prompt. */
169
228
  async evaluatePrompt(content, options) {
@@ -198,7 +257,11 @@ var DetectResource = class {
198
257
  }
199
258
  /** Run detectors without policy evaluation (POST /v1/detect). */
200
259
  async run(request, options) {
201
- return this._client._postJSON("/v1/detect", request, options?.timeout);
260
+ return withSpan(
261
+ "highflame.detect",
262
+ { "highflame.content_type": request.content_type ?? "prompt" },
263
+ () => this._client._postJSON("/v1/detect", request, options?.timeout)
264
+ );
202
265
  }
203
266
  };
204
267
  var DetectorsResource = class {
@@ -243,7 +306,7 @@ var Highflame = class {
243
306
  const baseUrl = options.baseUrl ?? SAAS_BASE_URL;
244
307
  const tokenUrl = options.tokenUrl ?? SAAS_TOKEN_URL;
245
308
  this.#baseUrl = baseUrl.replace(/\/$/, "");
246
- this.#tokenUrl = options.apiKey.startsWith("hf_sk") ? tokenUrl : void 0;
309
+ this.#tokenUrl = options.apiKey.startsWith("hf_sk") || options.apiKey.startsWith("hfa_") ? tokenUrl : void 0;
247
310
  this.#apiKey = options.apiKey;
248
311
  this.#timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
249
312
  this.#maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
@@ -369,6 +432,7 @@ var Highflame = class {
369
432
  this.#log.debug("Request body: %s", truncate(init.body));
370
433
  }
371
434
  const authHeaders = await this.getAuthHeaders();
435
+ injectTraceContext(authHeaders);
372
436
  if (idempotencyKey) {
373
437
  authHeaders["X-Idempotency-Key"] = idempotencyKey;
374
438
  }
@@ -430,6 +494,7 @@ var Highflame = class {
430
494
  const effectiveTimeout = overrideTimeout ?? this.#timeout;
431
495
  this.#log.debug("POST %s (stream)", path);
432
496
  const authHeaders = await this.getAuthHeaders();
497
+ injectTraceContext(authHeaders);
433
498
  const controller = new AbortController();
434
499
  const timer = setTimeout(() => controller.abort(), effectiveTimeout);
435
500
  let response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highflame/sdk",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "JavaScript/TypeScript SDK for Highflame AI guardrails",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -30,6 +30,7 @@
30
30
  "coverage": "vitest run --coverage"
31
31
  },
32
32
  "devDependencies": {
33
+ "@opentelemetry/api": "^1.9.0",
33
34
  "@types/node": "^20",
34
35
  "@vitest/coverage-v8": "^2",
35
36
  "eslint": "^9",
@@ -40,6 +41,14 @@
40
41
  "typescript-eslint": "^8",
41
42
  "vitest": "^2"
42
43
  },
44
+ "peerDependencies": {
45
+ "@opentelemetry/api": "^1.4.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "@opentelemetry/api": {
49
+ "optional": true
50
+ }
51
+ },
43
52
  "engines": {
44
53
  "node": ">=18"
45
54
  }