@revenium/openai 1.0.12 → 1.0.13

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.
Files changed (71) hide show
  1. package/.env.example +8 -8
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +57 -351
  4. package/dist/cjs/core/config/loader.js +1 -1
  5. package/dist/cjs/core/config/manager.js +2 -1
  6. package/dist/cjs/core/config/manager.js.map +1 -1
  7. package/dist/cjs/core/providers/detector.js +3 -3
  8. package/dist/cjs/core/providers/detector.js.map +1 -1
  9. package/dist/cjs/core/tracking/api-client.js +1 -1
  10. package/dist/cjs/core/tracking/payload-builder.js +17 -12
  11. package/dist/cjs/core/tracking/payload-builder.js.map +1 -1
  12. package/dist/cjs/index.js +23 -2
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/types/index.js.map +1 -1
  15. package/dist/cjs/utils/metadata-builder.js +12 -5
  16. package/dist/cjs/utils/metadata-builder.js.map +1 -1
  17. package/dist/cjs/utils/stop-reason-mapper.js +4 -0
  18. package/dist/cjs/utils/stop-reason-mapper.js.map +1 -1
  19. package/dist/cjs/utils/url-builder.js +3 -3
  20. package/dist/esm/core/config/loader.js +1 -1
  21. package/dist/esm/core/config/manager.js +2 -1
  22. package/dist/esm/core/config/manager.js.map +1 -1
  23. package/dist/esm/core/providers/detector.js +3 -3
  24. package/dist/esm/core/providers/detector.js.map +1 -1
  25. package/dist/esm/core/tracking/api-client.js +1 -1
  26. package/dist/esm/core/tracking/payload-builder.js +17 -12
  27. package/dist/esm/core/tracking/payload-builder.js.map +1 -1
  28. package/dist/esm/index.js +22 -2
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/types/index.js.map +1 -1
  31. package/dist/esm/utils/metadata-builder.js +12 -5
  32. package/dist/esm/utils/metadata-builder.js.map +1 -1
  33. package/dist/esm/utils/stop-reason-mapper.js +4 -0
  34. package/dist/esm/utils/stop-reason-mapper.js.map +1 -1
  35. package/dist/esm/utils/url-builder.js +3 -3
  36. package/dist/types/core/config/manager.d.ts.map +1 -1
  37. package/dist/types/core/tracking/payload-builder.d.ts.map +1 -1
  38. package/dist/types/index.d.ts +23 -2
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/types/index.d.ts +9 -13
  41. package/dist/types/types/index.d.ts.map +1 -1
  42. package/dist/types/types/openai-augmentation.d.ts +1 -2
  43. package/dist/types/types/openai-augmentation.d.ts.map +1 -1
  44. package/dist/types/utils/metadata-builder.d.ts +2 -1
  45. package/dist/types/utils/metadata-builder.d.ts.map +1 -1
  46. package/dist/types/utils/stop-reason-mapper.d.ts.map +1 -1
  47. package/dist/types/utils/url-builder.d.ts +3 -3
  48. package/examples/README.md +3 -41
  49. package/examples/azure-basic.ts +1 -1
  50. package/examples/azure-responses-basic.ts +3 -3
  51. package/examples/azure-responses-streaming.ts +3 -3
  52. package/examples/azure-streaming.ts +2 -2
  53. package/examples/getting_started.ts +1 -1
  54. package/examples/openai-basic.ts +2 -2
  55. package/examples/openai-function-calling.ts +2 -2
  56. package/examples/openai-responses-basic.ts +2 -2
  57. package/examples/openai-responses-streaming.ts +2 -2
  58. package/examples/openai-streaming.ts +1 -1
  59. package/examples/openai-vision.ts +2 -2
  60. package/package.json +1 -1
  61. package/src/core/config/loader.ts +1 -1
  62. package/src/core/config/manager.ts +2 -1
  63. package/src/core/providers/detector.ts +3 -3
  64. package/src/core/tracking/api-client.ts +1 -1
  65. package/src/core/tracking/payload-builder.ts +17 -12
  66. package/src/index.ts +27 -3
  67. package/src/types/index.ts +11 -14
  68. package/src/types/openai-augmentation.ts +1 -2
  69. package/src/utils/metadata-builder.ts +16 -7
  70. package/src/utils/stop-reason-mapper.ts +4 -0
  71. package/src/utils/url-builder.ts +3 -3
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Environment Variables:
8
8
  * REVENIUM_METERING_API_KEY=hak_your_api_key
9
- * REVENIUM_METERING_BASE_URL=https://api.revenium.io (optional)
9
+ * REVENIUM_METERING_BASE_URL=https://api.revenium.ai (optional)
10
10
  * OPENAI_API_KEY=sk_your_openai_key
11
11
  *
12
12
  * Simple Usage (auto-initialization):
@@ -77,7 +77,7 @@ export type {
77
77
  * ```typescript
78
78
  * const config: ReveniumConfig = {
79
79
  * reveniumApiKey: 'hak_your_api_key',
80
- * reveniumBaseUrl: 'https://api.revenium.io',
80
+ * reveniumBaseUrl: 'https://api.revenium.ai',
81
81
  * debug: true
82
82
  * };
83
83
  * ```
@@ -212,7 +212,7 @@ export function initializeRevenium(config: ReveniumConfig): {
212
212
  // Apply default base URL if not provided
213
213
  const configWithDefaults = {
214
214
  ...config,
215
- reveniumBaseUrl: config.reveniumBaseUrl || 'https://api.revenium.io',
215
+ reveniumBaseUrl: config.reveniumBaseUrl || 'https://api.revenium.ai',
216
216
  };
217
217
 
218
218
  validateConfig(configWithDefaults);
@@ -237,6 +237,30 @@ export function initializeRevenium(config: ReveniumConfig): {
237
237
  }
238
238
  }
239
239
 
240
+ /**
241
+ * Configure Revenium middleware manually
242
+ * Alias for initializeRevenium() with a more intuitive name
243
+ *
244
+ * @param config - Revenium configuration object
245
+ * @returns Initialization result with success status and message
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * import { configure } from '@revenium/openai';
250
+ *
251
+ * configure({
252
+ * reveniumApiKey: 'hak_your_api_key',
253
+ * reveniumBaseUrl: 'https://api.revenium.ai',
254
+ * });
255
+ * ```
256
+ */
257
+ export function configure(config: ReveniumConfig): {
258
+ success: boolean;
259
+ message: string;
260
+ } {
261
+ return initializeRevenium(config);
262
+ }
263
+
240
264
  /**
241
265
  * Initialize Revenium middleware from environment variables
242
266
  */
@@ -80,7 +80,7 @@ export interface Subscriber {
80
80
  * productId: 'chat-assistant',
81
81
  * taskType: 'customer-support',
82
82
  * traceId: 'trace-789',
83
- * responseQualityScore: 0.95,
83
+ * responseQualityScore: 0.95, // 0.0-1.0 scale per API spec
84
84
  * agent: 'support-bot-v2'
85
85
  * };
86
86
  * ```
@@ -98,19 +98,14 @@ export interface UsageMetadata {
98
98
 
99
99
  /** Task type classification (e.g., 'chat', 'summarization', 'translation') */
100
100
  taskType?: string;
101
- /** Unique task identifier for request correlation */
102
- taskId?: string;
103
101
  /** Distributed tracing identifier for request tracking across services */
104
102
  traceId?: string;
105
103
 
106
- /** Quality score for response evaluation (0.0 to 1.0) */
104
+ /** Quality score for response evaluation (0.0-1.0 scale per API spec) */
107
105
  responseQualityScore?: number;
108
106
 
109
107
  /** Agent or model variant identifier for A/B testing and performance tracking */
110
108
  agent?: string;
111
-
112
- /** Allow additional custom fields for extensibility */
113
- [key: string]: unknown;
114
109
  }
115
110
 
116
111
  /**
@@ -214,7 +209,7 @@ export interface AzureConfig {
214
209
  * ```typescript
215
210
  * const config: ReveniumConfig = {
216
211
  * reveniumApiKey: 'hak_your_revenium_api_key',
217
- * reveniumBaseUrl: 'https://api.revenium.io',
212
+ * reveniumBaseUrl: 'https://api.revenium.ai',
218
213
  * debug: true,
219
214
  * openaiApiKey: process.env.OPENAI_API_KEY
220
215
  * };
@@ -223,7 +218,7 @@ export interface AzureConfig {
223
218
  export interface ReveniumConfig {
224
219
  /** Revenium API key for authentication (required) */
225
220
  reveniumApiKey: string;
226
- /** Revenium API base URL (optional, defaults to https://api.revenium.io) */
221
+ /** Revenium API base URL (optional, defaults to https://api.revenium.ai) */
227
222
  reveniumBaseUrl?: string;
228
223
  /** Enable debug logging (optional, defaults to false) */
229
224
  debug?: boolean;
@@ -268,7 +263,7 @@ export interface Logger {
268
263
  export interface ReveniumPayload {
269
264
  // Core identification
270
265
  transactionId: string;
271
- operationType: 'CHAT' | 'EMBED';
266
+ operationType: 'CHAT' | 'GENERATE' | 'EMBED' | 'CLASSIFY' | 'SUMMARIZE' | 'TRANSLATE' | 'OTHER';
272
267
  costType: 'AI';
273
268
 
274
269
  // Model and provider info
@@ -287,14 +282,16 @@ export interface ReveniumPayload {
287
282
  inputTokenCount: number;
288
283
  outputTokenCount: number;
289
284
  totalTokenCount: number;
290
- reasoningTokenCount: number;
291
- cacheCreationTokenCount: number;
292
- cacheReadTokenCount: number;
285
+ // API Spec: https://revenium.readme.io/reference/meter_ai_completion
286
+ // "Leave null for models without reasoning capabilities" - NOT in required fields
287
+ reasoningTokenCount: number | undefined;
288
+ cacheCreationTokenCount: number | undefined; // Undefined when provider doesn't report
289
+ cacheReadTokenCount: number | undefined; // Undefined when provider doesn't report
293
290
 
294
291
  // Chat-specific fields
295
292
  stopReason: string;
296
293
  isStreamed: boolean;
297
- timeToFirstToken?: number;
294
+ timeToFirstToken?: number | undefined; // Undefined when not tracking TTFB
298
295
 
299
296
  // Cost information (calculated by backend)
300
297
  inputTokenCost?: number;
@@ -154,8 +154,7 @@ declare module 'openai/resources/embeddings' {
154
154
  * },
155
155
  * organizationId: 'my-company',
156
156
  * productId: 'vector-search',
157
- * taskType: 'document-indexing',
158
- * taskId: 'batch-index-2024-01'
157
+ * taskType: 'document-indexing'
159
158
  * }
160
159
  * ```
161
160
  *
@@ -37,7 +37,7 @@ const METADATA_FIELD_MAP: MetadataFieldConfig[] = [
37
37
  {
38
38
  source: 'responseQualityScore',
39
39
  transform: (value: unknown) => {
40
- // Ensure quality score is between 0 and 1
40
+ // Ensure quality score is between 0.0 and 1.0 (API spec requirement)
41
41
  if (typeof value === 'number') return Math.max(0, Math.min(1, value));
42
42
  return value;
43
43
  },
@@ -111,8 +111,10 @@ export function validateMetadata(
111
111
  // Check for common issues
112
112
  if (usageMetadata.responseQualityScore) {
113
113
  const score = usageMetadata.responseQualityScore;
114
+ // API Spec: https://revenium.readme.io/reference/meter_ai_completion (responseQualityScore)
115
+ // "typically on a 0.0-1.0 scale"
114
116
  if (typeof score !== 'number' || score < 0 || score > 1) {
115
- warnings.push('responseQualityScore should be a number between 0 and 1');
117
+ warnings.push('responseQualityScore should be a number between 0.0 and 1.0');
116
118
  }
117
119
  }
118
120
 
@@ -164,17 +166,23 @@ export function extractMetadata<T extends Record<string, unknown>>(
164
166
 
165
167
  /**
166
168
  * Create a metadata context for consistent logging
169
+ * Uses sanitization to protect PII (emails are masked)
167
170
  *
168
171
  * @param usageMetadata - Source metadata
169
- * @returns Logging context object
172
+ * @returns Logging context object with sanitized PII
170
173
  */
171
174
  export function createLoggingContext(usageMetadata?: UsageMetadata): Record<string, unknown> {
172
175
  if (!usageMetadata) return {};
176
+
177
+ // Use sanitizer to protect PII in logs
178
+ const sanitized = sanitizeMetadataForLogging(usageMetadata);
179
+ const sanitizedSubscriber = sanitized.subscriber as { id?: string; email?: string; credential?: unknown };
180
+
173
181
  return {
174
182
  traceId: usageMetadata.traceId,
175
183
  taskType: usageMetadata.taskType,
176
184
  subscriberId: usageMetadata.subscriber?.id,
177
- subscriberEmail: usageMetadata.subscriber?.email,
185
+ subscriberEmail: sanitizedSubscriber?.email, // ← Now masked: us***@example.com
178
186
  organizationId: usageMetadata.organizationId,
179
187
  productId: usageMetadata.productId,
180
188
  agent: usageMetadata.agent,
@@ -193,18 +201,19 @@ export function sanitizeMetadataForLogging(usageMetadata?: UsageMetadata): Recor
193
201
  // Create a copy and handle nested subscriber object
194
202
  const { subscriber, ...safeMetadata } = usageMetadata;
195
203
 
196
- const result = { ...safeMetadata };
204
+ const result: Record<string, unknown> = { ...safeMetadata };
197
205
 
198
206
  // Sanitize subscriber object if present
199
207
  if (subscriber) {
200
- const sanitizedSubscriber: any = {};
208
+ const sanitizedSubscriber: Record<string, unknown> = {};
201
209
 
202
210
  if (subscriber.id) {
203
211
  sanitizedSubscriber.id = subscriber.id;
204
212
  }
205
213
 
206
214
  if (subscriber.email) {
207
- sanitizedSubscriber.email = subscriber.email.replace(/(.{2}).*(@.*)/, '$1***$2');
215
+ // Mask email: handles single-char emails (a@x.com → a***@x.com)
216
+ sanitizedSubscriber.email = subscriber.email.replace(/(.{1,2}).*(@.*)/, '$1***$2');
208
217
  }
209
218
 
210
219
  if (subscriber.credential) {
@@ -18,8 +18,12 @@ const STOP_REASON_MAP: Record<string, string> = {
18
18
  timeout: 'TIMEOUT',
19
19
  length: 'TOKEN_LIMIT',
20
20
  max_tokens: 'TOKEN_LIMIT',
21
+ cost_limit: 'COST_LIMIT',
22
+ completion_limit: 'COMPLETION_LIMIT',
21
23
  content_filter: 'ERROR',
22
24
  error: 'ERROR',
25
+ cancelled: 'CANCELLED',
26
+ canceled: 'CANCELLED', // Handle both spellings
23
27
 
24
28
  // Anthropic stop reasons (for consistency across middleware)
25
29
  end_turn: 'END',
@@ -15,9 +15,9 @@
15
15
  * - If URL has neither → append /meter/v2 and endpoint
16
16
  *
17
17
  * Examples:
18
- * - 'https://api.revenium.io' + '/ai/completions' → 'https://api.revenium.io/meter/v2/ai/completions'
19
- * - 'https://api.revenium.io/meter' + '/ai/completions' → 'https://api.revenium.io/meter/v2/ai/completions'
20
- * - 'https://api.revenium.io/meter/v2' + '/ai/completions' → 'https://api.revenium.io/meter/v2/ai/completions'
18
+ * - 'https://api.revenium.ai' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
19
+ * - 'https://api.revenium.ai/meter' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
20
+ * - 'https://api.revenium.ai/meter/v2' + '/ai/completions' → 'https://api.revenium.ai/meter/v2/ai/completions'
21
21
  *
22
22
  * @param baseUrl - The base URL from configuration (may include /meter or /meter/v2)
23
23
  * @param endpoint - The API endpoint to append (e.g., '/ai/completions')