@revenium/openai 1.0.12 → 1.0.14

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 (236) hide show
  1. package/.env.example +10 -15
  2. package/CHANGELOG.md +59 -0
  3. package/CODE_OF_CONDUCT.md +57 -0
  4. package/CONTRIBUTING.md +38 -0
  5. package/README.md +109 -515
  6. package/SECURITY.md +34 -0
  7. package/dist/cjs/core/client/index.js +14 -0
  8. package/dist/cjs/core/client/index.js.map +1 -0
  9. package/dist/cjs/core/client/manager.js +109 -0
  10. package/dist/cjs/core/client/manager.js.map +1 -0
  11. package/dist/cjs/core/config/azure-config.js +5 -17
  12. package/dist/cjs/core/config/azure-config.js.map +1 -1
  13. package/dist/cjs/core/config/index.js +2 -2
  14. package/dist/cjs/core/config/index.js.map +1 -1
  15. package/dist/cjs/core/config/loader.js +34 -14
  16. package/dist/cjs/core/config/loader.js.map +1 -1
  17. package/dist/cjs/core/config/manager.js +12 -5
  18. package/dist/cjs/core/config/manager.js.map +1 -1
  19. package/dist/cjs/core/config/validator.js +3 -45
  20. package/dist/cjs/core/config/validator.js.map +1 -1
  21. package/dist/cjs/core/middleware/index.js +17 -0
  22. package/dist/cjs/core/middleware/index.js.map +1 -0
  23. package/dist/cjs/core/middleware/interfaces.js +361 -0
  24. package/dist/cjs/core/middleware/interfaces.js.map +1 -0
  25. package/dist/cjs/core/middleware/revenium-client.js +142 -0
  26. package/dist/cjs/core/middleware/revenium-client.js.map +1 -0
  27. package/dist/cjs/core/providers/detector.js +45 -23
  28. package/dist/cjs/core/providers/detector.js.map +1 -1
  29. package/dist/cjs/core/providers/index.js +2 -1
  30. package/dist/cjs/core/providers/index.js.map +1 -1
  31. package/dist/cjs/core/tracking/api-client.js +14 -13
  32. package/dist/cjs/core/tracking/api-client.js.map +1 -1
  33. package/dist/cjs/core/tracking/payload-builder.js +30 -35
  34. package/dist/cjs/core/tracking/payload-builder.js.map +1 -1
  35. package/dist/cjs/core/tracking/usage-tracker.js +22 -18
  36. package/dist/cjs/core/tracking/usage-tracker.js.map +1 -1
  37. package/dist/cjs/index.js +26 -174
  38. package/dist/cjs/index.js.map +1 -1
  39. package/dist/cjs/types/index.js +0 -8
  40. package/dist/cjs/types/index.js.map +1 -1
  41. package/dist/cjs/types/openai-augmentation.js +0 -49
  42. package/dist/cjs/types/openai-augmentation.js.map +1 -1
  43. package/dist/cjs/utils/constants.js +17 -20
  44. package/dist/cjs/utils/constants.js.map +1 -1
  45. package/dist/cjs/utils/error-handler.js +18 -14
  46. package/dist/cjs/utils/error-handler.js.map +1 -1
  47. package/dist/cjs/utils/metadata-builder.js +27 -19
  48. package/dist/cjs/utils/metadata-builder.js.map +1 -1
  49. package/dist/cjs/utils/provider-detection.js +25 -28
  50. package/dist/cjs/utils/provider-detection.js.map +1 -1
  51. package/dist/cjs/utils/stop-reason-mapper.js +4 -0
  52. package/dist/cjs/utils/stop-reason-mapper.js.map +1 -1
  53. package/dist/cjs/utils/url-builder.js +3 -3
  54. package/dist/esm/core/client/index.js +6 -0
  55. package/dist/esm/core/client/index.js.map +1 -0
  56. package/dist/esm/core/client/manager.js +102 -0
  57. package/dist/esm/core/client/manager.js.map +1 -0
  58. package/dist/esm/core/config/azure-config.js +6 -18
  59. package/dist/esm/core/config/azure-config.js.map +1 -1
  60. package/dist/esm/core/config/index.js +5 -4
  61. package/dist/esm/core/config/index.js.map +1 -1
  62. package/dist/esm/core/config/loader.js +33 -13
  63. package/dist/esm/core/config/loader.js.map +1 -1
  64. package/dist/esm/core/config/manager.js +14 -7
  65. package/dist/esm/core/config/manager.js.map +1 -1
  66. package/dist/esm/core/config/validator.js +3 -44
  67. package/dist/esm/core/config/validator.js.map +1 -1
  68. package/dist/esm/core/middleware/index.js +8 -0
  69. package/dist/esm/core/middleware/index.js.map +1 -0
  70. package/dist/esm/core/middleware/interfaces.js +353 -0
  71. package/dist/esm/core/middleware/interfaces.js.map +1 -0
  72. package/dist/esm/core/middleware/revenium-client.js +105 -0
  73. package/dist/esm/core/middleware/revenium-client.js.map +1 -0
  74. package/dist/esm/core/providers/detector.js +43 -22
  75. package/dist/esm/core/providers/detector.js.map +1 -1
  76. package/dist/esm/core/providers/index.js +2 -2
  77. package/dist/esm/core/providers/index.js.map +1 -1
  78. package/dist/esm/core/tracking/api-client.js +13 -12
  79. package/dist/esm/core/tracking/api-client.js.map +1 -1
  80. package/dist/esm/core/tracking/payload-builder.js +31 -36
  81. package/dist/esm/core/tracking/payload-builder.js.map +1 -1
  82. package/dist/esm/core/tracking/usage-tracker.js +24 -20
  83. package/dist/esm/core/tracking/usage-tracker.js.map +1 -1
  84. package/dist/esm/index.js +9 -157
  85. package/dist/esm/index.js.map +1 -1
  86. package/dist/esm/types/index.js +2 -10
  87. package/dist/esm/types/index.js.map +1 -1
  88. package/dist/esm/types/openai-augmentation.js +0 -49
  89. package/dist/esm/types/openai-augmentation.js.map +1 -1
  90. package/dist/esm/utils/constants.js +16 -19
  91. package/dist/esm/utils/constants.js.map +1 -1
  92. package/dist/esm/utils/error-handler.js +19 -15
  93. package/dist/esm/utils/error-handler.js.map +1 -1
  94. package/dist/esm/utils/metadata-builder.js +27 -19
  95. package/dist/esm/utils/metadata-builder.js.map +1 -1
  96. package/dist/esm/utils/provider-detection.js +26 -29
  97. package/dist/esm/utils/provider-detection.js.map +1 -1
  98. package/dist/esm/utils/stop-reason-mapper.js +4 -0
  99. package/dist/esm/utils/stop-reason-mapper.js.map +1 -1
  100. package/dist/esm/utils/url-builder.js +3 -3
  101. package/dist/types/core/client/index.d.ts +6 -0
  102. package/dist/types/core/client/index.d.ts.map +1 -0
  103. package/dist/types/core/client/manager.d.ts +32 -0
  104. package/dist/types/core/client/manager.d.ts.map +1 -0
  105. package/dist/types/core/config/azure-config.d.ts +2 -2
  106. package/dist/types/core/config/azure-config.d.ts.map +1 -1
  107. package/dist/types/core/config/index.d.ts +4 -4
  108. package/dist/types/core/config/index.d.ts.map +1 -1
  109. package/dist/types/core/config/loader.d.ts +3 -1
  110. package/dist/types/core/config/loader.d.ts.map +1 -1
  111. package/dist/types/core/config/manager.d.ts +1 -1
  112. package/dist/types/core/config/manager.d.ts.map +1 -1
  113. package/dist/types/core/config/validator.d.ts +1 -12
  114. package/dist/types/core/config/validator.d.ts.map +1 -1
  115. package/dist/types/core/middleware/index.d.ts +8 -0
  116. package/dist/types/core/middleware/index.d.ts.map +1 -0
  117. package/dist/types/core/middleware/interfaces.d.ts +74 -0
  118. package/dist/types/core/middleware/interfaces.d.ts.map +1 -0
  119. package/dist/types/core/middleware/revenium-client.d.ts +58 -0
  120. package/dist/types/core/middleware/revenium-client.d.ts.map +1 -0
  121. package/dist/types/core/providers/detector.d.ts +9 -2
  122. package/dist/types/core/providers/detector.d.ts.map +1 -1
  123. package/dist/types/core/providers/index.d.ts +2 -2
  124. package/dist/types/core/providers/index.d.ts.map +1 -1
  125. package/dist/types/core/tracking/api-client.d.ts +1 -1
  126. package/dist/types/core/tracking/api-client.d.ts.map +1 -1
  127. package/dist/types/core/tracking/payload-builder.d.ts +3 -3
  128. package/dist/types/core/tracking/payload-builder.d.ts.map +1 -1
  129. package/dist/types/core/tracking/usage-tracker.d.ts +2 -2
  130. package/dist/types/core/tracking/usage-tracker.d.ts.map +1 -1
  131. package/dist/types/index.d.ts +11 -114
  132. package/dist/types/index.d.ts.map +1 -1
  133. package/dist/types/types/function-parameters.d.ts +2 -23
  134. package/dist/types/types/function-parameters.d.ts.map +1 -1
  135. package/dist/types/types/index.d.ts +17 -115
  136. package/dist/types/types/index.d.ts.map +1 -1
  137. package/dist/types/types/openai-augmentation.d.ts +4 -139
  138. package/dist/types/types/openai-augmentation.d.ts.map +1 -1
  139. package/dist/types/utils/constants.d.ts +7 -1
  140. package/dist/types/utils/constants.d.ts.map +1 -1
  141. package/dist/types/utils/error-handler.d.ts +2 -2
  142. package/dist/types/utils/error-handler.d.ts.map +1 -1
  143. package/dist/types/utils/metadata-builder.d.ts +4 -3
  144. package/dist/types/utils/metadata-builder.d.ts.map +1 -1
  145. package/dist/types/utils/provider-detection.d.ts +3 -3
  146. package/dist/types/utils/provider-detection.d.ts.map +1 -1
  147. package/dist/types/utils/stop-reason-mapper.d.ts.map +1 -1
  148. package/dist/types/utils/url-builder.d.ts +3 -3
  149. package/examples/README.md +270 -224
  150. package/examples/azure/basic.ts +62 -0
  151. package/examples/azure/responses-basic.ts +45 -0
  152. package/examples/azure/responses-stream.ts +61 -0
  153. package/examples/azure/stream.ts +56 -0
  154. package/examples/getting_started.ts +31 -43
  155. package/examples/openai/basic.ts +45 -0
  156. package/examples/openai/metadata.ts +67 -0
  157. package/examples/openai/responses-basic.ts +44 -0
  158. package/examples/openai/responses-embed.ts +34 -0
  159. package/examples/openai/responses-streaming.ts +63 -0
  160. package/examples/openai/streaming.ts +59 -0
  161. package/package.json +20 -13
  162. package/dist/cjs/core/wrapper/index.js +0 -15
  163. package/dist/cjs/core/wrapper/index.js.map +0 -1
  164. package/dist/cjs/core/wrapper/instance-patcher.js +0 -202
  165. package/dist/cjs/core/wrapper/instance-patcher.js.map +0 -1
  166. package/dist/cjs/core/wrapper/request-handler.js +0 -317
  167. package/dist/cjs/core/wrapper/request-handler.js.map +0 -1
  168. package/dist/cjs/core/wrapper/stream-wrapper.js +0 -82
  169. package/dist/cjs/core/wrapper/stream-wrapper.js.map +0 -1
  170. package/dist/cjs/utils/azure-model-resolver.js +0 -211
  171. package/dist/cjs/utils/azure-model-resolver.js.map +0 -1
  172. package/dist/cjs/utils/request-handler-factory.js +0 -185
  173. package/dist/cjs/utils/request-handler-factory.js.map +0 -1
  174. package/dist/esm/core/wrapper/index.js +0 -9
  175. package/dist/esm/core/wrapper/index.js.map +0 -1
  176. package/dist/esm/core/wrapper/instance-patcher.js +0 -199
  177. package/dist/esm/core/wrapper/instance-patcher.js.map +0 -1
  178. package/dist/esm/core/wrapper/request-handler.js +0 -310
  179. package/dist/esm/core/wrapper/request-handler.js.map +0 -1
  180. package/dist/esm/core/wrapper/stream-wrapper.js +0 -79
  181. package/dist/esm/core/wrapper/stream-wrapper.js.map +0 -1
  182. package/dist/esm/utils/azure-model-resolver.js +0 -204
  183. package/dist/esm/utils/azure-model-resolver.js.map +0 -1
  184. package/dist/esm/utils/request-handler-factory.js +0 -146
  185. package/dist/esm/utils/request-handler-factory.js.map +0 -1
  186. package/dist/types/core/wrapper/index.d.ts +0 -8
  187. package/dist/types/core/wrapper/index.d.ts.map +0 -1
  188. package/dist/types/core/wrapper/instance-patcher.d.ts +0 -33
  189. package/dist/types/core/wrapper/instance-patcher.d.ts.map +0 -1
  190. package/dist/types/core/wrapper/request-handler.d.ts +0 -29
  191. package/dist/types/core/wrapper/request-handler.d.ts.map +0 -1
  192. package/dist/types/core/wrapper/stream-wrapper.d.ts +0 -13
  193. package/dist/types/core/wrapper/stream-wrapper.d.ts.map +0 -1
  194. package/dist/types/utils/azure-model-resolver.d.ts +0 -41
  195. package/dist/types/utils/azure-model-resolver.d.ts.map +0 -1
  196. package/dist/types/utils/request-handler-factory.d.ts +0 -81
  197. package/dist/types/utils/request-handler-factory.d.ts.map +0 -1
  198. package/examples/azure-basic.ts +0 -206
  199. package/examples/azure-responses-basic.ts +0 -233
  200. package/examples/azure-responses-streaming.ts +0 -255
  201. package/examples/azure-streaming.ts +0 -209
  202. package/examples/openai-basic.ts +0 -147
  203. package/examples/openai-function-calling.ts +0 -259
  204. package/examples/openai-responses-basic.ts +0 -212
  205. package/examples/openai-responses-streaming.ts +0 -232
  206. package/examples/openai-streaming.ts +0 -172
  207. package/examples/openai-vision.ts +0 -289
  208. package/src/core/config/azure-config.ts +0 -72
  209. package/src/core/config/index.ts +0 -23
  210. package/src/core/config/loader.ts +0 -66
  211. package/src/core/config/manager.ts +0 -94
  212. package/src/core/config/validator.ts +0 -89
  213. package/src/core/providers/detector.ts +0 -159
  214. package/src/core/providers/index.ts +0 -16
  215. package/src/core/tracking/api-client.ts +0 -78
  216. package/src/core/tracking/index.ts +0 -21
  217. package/src/core/tracking/payload-builder.ts +0 -132
  218. package/src/core/tracking/usage-tracker.ts +0 -189
  219. package/src/core/wrapper/index.ts +0 -9
  220. package/src/core/wrapper/instance-patcher.ts +0 -288
  221. package/src/core/wrapper/request-handler.ts +0 -423
  222. package/src/core/wrapper/stream-wrapper.ts +0 -100
  223. package/src/index.ts +0 -336
  224. package/src/types/function-parameters.ts +0 -251
  225. package/src/types/index.ts +0 -313
  226. package/src/types/openai-augmentation.ts +0 -233
  227. package/src/types/responses-api.ts +0 -308
  228. package/src/utils/azure-model-resolver.ts +0 -220
  229. package/src/utils/constants.ts +0 -21
  230. package/src/utils/error-handler.ts +0 -251
  231. package/src/utils/metadata-builder.ts +0 -219
  232. package/src/utils/provider-detection.ts +0 -257
  233. package/src/utils/request-handler-factory.ts +0 -285
  234. package/src/utils/stop-reason-mapper.ts +0 -74
  235. package/src/utils/type-guards.ts +0 -202
  236. package/src/utils/url-builder.ts +0 -68
@@ -1,132 +0,0 @@
1
- /**
2
- * Payload Builder Module
3
- *
4
- * Handles construction of Revenium API payloads.
5
- * Extracted from tracking.ts for single responsibility.
6
- */
7
-
8
- import { randomUUID } from 'crypto';
9
- import { ReveniumPayload, ProviderInfo } from '../../types/index.js';
10
- import {
11
- OpenAIChatResponse,
12
- OpenAIEmbeddingResponse,
13
- OpenAIChatRequest,
14
- OpenAIEmbeddingRequest,
15
- } from '../../types/function-parameters.js';
16
- import { getLogger } from '../config/index.js';
17
- import { mapStopReason } from '../../utils/stop-reason-mapper.js';
18
- import { buildMetadataFields } from '../../utils/metadata-builder.js';
19
- import { resolveAzureModelName } from '../../utils/azure-model-resolver.js';
20
- import { getProviderMetadata } from '../providers/index.js';
21
-
22
- // Global logger
23
- const logger = getLogger();
24
-
25
- /**
26
- * Build payload for Revenium API
27
- *
28
- * This shared payload builder eliminates payload duplication between
29
- * chat completions and embeddings. Handles both CHAT and EMBED operation types.
30
- *
31
- * @param operationType - Type of operation (CHAT or EMBED)
32
- * @param response - API response from OpenAI/Azure
33
- * @param request - Original request parameters
34
- * @param startTime - Request start timestamp
35
- * @param duration - Request duration in milliseconds
36
- * @param providerInfo - Provider information for Azure support
37
- * @returns Constructed payload for Revenium API
38
- */
39
- export function buildPayload(
40
- operationType: 'CHAT' | 'EMBED',
41
- response: OpenAIChatResponse | OpenAIEmbeddingResponse,
42
- request: OpenAIChatRequest | OpenAIEmbeddingRequest,
43
- startTime: number,
44
- duration: number,
45
- providerInfo?: ProviderInfo
46
- ): ReveniumPayload {
47
- const now = new Date().toISOString();
48
- const requestTime = new Date(startTime).toISOString();
49
- const usage = response.usage;
50
-
51
- // Resolve model name for Azure deployments
52
- const originalModel = response.model;
53
- const resolvedModel = providerInfo?.isAzure
54
- ? resolveAzureModelName(originalModel)
55
- : originalModel;
56
-
57
- // Get provider metadata
58
- const providerMetadata = providerInfo
59
- ? getProviderMetadata(providerInfo)
60
- : { provider: 'OPENAI', modelSource: 'OPENAI' };
61
-
62
- if (providerInfo?.isAzure && resolvedModel !== originalModel) {
63
- logger.debug('Azure model name resolved for pricing', {
64
- deployment: originalModel,
65
- resolved: resolvedModel,
66
- provider: providerMetadata.provider,
67
- });
68
- }
69
-
70
- // Build metadata fields using utility (eliminates repetitive spreading)
71
- const metadataFields = buildMetadataFields(request.usageMetadata);
72
-
73
- // Common fields for all operations
74
- const commonPayload = {
75
- costType: 'AI' as const,
76
- model: resolvedModel, // Use resolved model name for accurate pricing
77
- responseTime: now,
78
- requestDuration: duration,
79
- provider: providerMetadata.provider,
80
- modelSource: providerMetadata.modelSource,
81
- requestTime,
82
- completionStartTime: now,
83
-
84
- // Common token counts
85
- inputTokenCount: usage.prompt_tokens,
86
- totalTokenCount: usage.total_tokens,
87
-
88
- // Metadata fields (processed by utility)
89
- ...metadataFields,
90
-
91
- // Fixed middleware source identifier
92
- middlewareSource: 'nodejs',
93
-
94
- // Backend calculates costs
95
- inputTokenCost: undefined,
96
- outputTokenCost: undefined,
97
- totalCost: undefined,
98
- };
99
-
100
- // Operation-specific fields
101
-
102
- if (operationType !== 'CHAT') {
103
- // For embeddings, we don't need the response cast since we use commonPayload
104
- return {
105
- ...commonPayload,
106
- operationType: 'EMBED',
107
- transactionId: `embed-${randomUUID()}`,
108
- outputTokenCount: 0,
109
- reasoningTokenCount: 0,
110
- cacheCreationTokenCount: 0,
111
- cacheReadTokenCount: 0,
112
- stopReason: 'END',
113
- isStreamed: false,
114
- timeToFirstToken: 0,
115
- };
116
- }
117
- const chatResponse = response as OpenAIChatResponse;
118
- const chatUsage = chatResponse.usage;
119
-
120
- return {
121
- ...commonPayload,
122
- operationType: 'CHAT',
123
- transactionId: chatResponse.id || `chat-${randomUUID()}`,
124
- outputTokenCount: chatUsage.completion_tokens || 0,
125
- reasoningTokenCount: chatUsage.reasoning_tokens || 0,
126
- cacheCreationTokenCount: 0,
127
- cacheReadTokenCount: chatUsage.cached_tokens || 0,
128
- stopReason: mapStopReason(chatResponse.choices?.[0]?.finish_reason, logger),
129
- isStreamed: Boolean((request as OpenAIChatRequest).stream),
130
- timeToFirstToken: (request as OpenAIChatRequest).stream ? 0 : duration,
131
- };
132
- }
@@ -1,189 +0,0 @@
1
- /**
2
- * Usage Tracker Module
3
- *
4
- * High-level tracking functions that combine payload building and API communication.
5
- * Extracted from tracking.ts for better organization.
6
- */
7
-
8
- import { randomUUID } from 'crypto';
9
- import { UsageMetadata, ProviderInfo } from '../../types/index.js';
10
- import {
11
- OpenAIChatResponse,
12
- OpenAIEmbeddingResponse,
13
- OpenAIChatRequest,
14
- OpenAIEmbeddingRequest,
15
- } from '../../types/function-parameters.js';
16
- import { getLogger } from '../config/index.js';
17
- import { sendToRevenium } from './api-client.js';
18
- import { buildPayload } from './payload-builder.js';
19
- import { safeAsyncOperation } from '../../utils/error-handler.js';
20
-
21
- // Global logger
22
- const logger = getLogger();
23
-
24
- /**
25
- * Chat completions tracking - now a thin wrapper with Azure support
26
- */
27
- export async function sendReveniumMetrics(
28
- response: OpenAIChatResponse,
29
- request: OpenAIChatRequest,
30
- startTime: number,
31
- duration: number,
32
- providerInfo?: ProviderInfo
33
- ): Promise<void> {
34
- await safeAsyncOperation(
35
- async () => {
36
- const payload = buildPayload('CHAT', response, request, startTime, duration, providerInfo);
37
- await sendToRevenium(payload);
38
- },
39
- 'Chat completion tracking',
40
- {
41
- logError: true,
42
- rethrow: false, // Don't rethrow to maintain fire-and-forget behavior
43
- messagePrefix: 'Chat completion tracking failed: ',
44
- },
45
- logger
46
- );
47
- }
48
-
49
- /**
50
- * Embeddings tracking - now a thin wrapper with Azure support
51
- */
52
- export async function sendReveniumEmbeddingsMetrics(
53
- response: OpenAIEmbeddingResponse,
54
- request: OpenAIEmbeddingRequest,
55
- startTime: number,
56
- duration: number,
57
- providerInfo?: ProviderInfo
58
- ): Promise<void> {
59
- await safeAsyncOperation(
60
- async () => {
61
- const payload = buildPayload('EMBED', response, request, startTime, duration, providerInfo);
62
- await sendToRevenium(payload);
63
- },
64
- 'Embeddings tracking',
65
- {
66
- logError: true,
67
- rethrow: false, // Don't rethrow to maintain fire-and-forget behavior
68
- messagePrefix: 'Embeddings tracking failed: ',
69
- },
70
- logger
71
- );
72
- }
73
-
74
- /**
75
- * Fire-and-forget wrapper for chat completions with Azure support
76
- */
77
- export function trackUsageAsync(trackingData: {
78
- requestId: string;
79
- model: string;
80
- promptTokens: number;
81
- completionTokens: number;
82
- totalTokens: number;
83
- reasoningTokens?: number;
84
- cachedTokens?: number;
85
- duration: number;
86
- finishReason: string | null;
87
- usageMetadata?: UsageMetadata;
88
- isStreamed?: boolean;
89
- timeToFirstToken?: number;
90
- providerInfo?: ProviderInfo;
91
- }): void {
92
- const mockResponse = {
93
- id: trackingData.requestId,
94
- model: trackingData.model,
95
- usage: {
96
- prompt_tokens: trackingData.promptTokens,
97
- completion_tokens: trackingData.completionTokens,
98
- total_tokens: trackingData.totalTokens,
99
- ...(trackingData.reasoningTokens && { reasoning_tokens: trackingData.reasoningTokens }),
100
- ...(trackingData.cachedTokens && { cached_tokens: trackingData.cachedTokens }),
101
- },
102
- choices: [
103
- {
104
- finish_reason: trackingData.finishReason,
105
- },
106
- ],
107
- };
108
-
109
- const mockRequest: OpenAIChatRequest = {
110
- model: trackingData.model,
111
- messages: [], // Mock empty messages array for type compliance
112
- usageMetadata: trackingData.usageMetadata,
113
- stream: trackingData.isStreamed,
114
- };
115
-
116
- const startTime = Date.now() - trackingData.duration;
117
-
118
- sendReveniumMetrics(
119
- mockResponse,
120
- mockRequest,
121
- startTime,
122
- trackingData.duration,
123
- trackingData.providerInfo
124
- )
125
- .then(() => {
126
- logger.debug('Usage tracking completed successfully', {
127
- requestId: trackingData.requestId,
128
- model: trackingData.model,
129
- totalTokens: trackingData.totalTokens,
130
- isStreamed: trackingData.isStreamed,
131
- });
132
- })
133
- .catch(error => {
134
- logger.warn('Usage tracking failed', {
135
- error: error instanceof Error ? error.message : String(error),
136
- requestId: trackingData.requestId,
137
- model: trackingData.model,
138
- });
139
- });
140
- }
141
-
142
- /**
143
- * Fire-and-forget wrapper for embeddings with Azure support
144
- */
145
- export function trackEmbeddingsUsageAsync(trackingData: {
146
- transactionId: string;
147
- model: string;
148
- promptTokens: number;
149
- totalTokens: number;
150
- duration: number;
151
- usageMetadata?: UsageMetadata;
152
- requestStartTime: number;
153
- providerInfo?: ProviderInfo;
154
- }): void {
155
- const mockResponse: OpenAIEmbeddingResponse = {
156
- model: trackingData.model,
157
- usage: {
158
- prompt_tokens: trackingData.promptTokens,
159
- total_tokens: trackingData.totalTokens,
160
- },
161
- data: [], // Mock empty data array for type compliance
162
- object: 'list',
163
- };
164
-
165
- const mockRequest: OpenAIEmbeddingRequest = {
166
- model: trackingData.model,
167
- input: '', // Mock empty input for type compliance
168
- usageMetadata: trackingData.usageMetadata,
169
- };
170
-
171
- sendReveniumEmbeddingsMetrics(
172
- mockResponse,
173
- mockRequest,
174
- trackingData.requestStartTime,
175
- trackingData.duration,
176
- trackingData.providerInfo
177
- )
178
- .then(() => {
179
- logger.debug('Embeddings tracking completed successfully', {
180
- transactionId: trackingData.transactionId,
181
- });
182
- })
183
- .catch(error => {
184
- logger.warn('Embeddings tracking failed', {
185
- error: error instanceof Error ? error.message : String(error),
186
- transactionId: trackingData.transactionId,
187
- });
188
- });
189
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Wrapper module - Main exports
3
- *
4
- * This module provides a clean interface for OpenAI client wrapping,
5
- * separating concerns into focused sub-modules.
6
- */
7
-
8
- // Re-export all wrapper functionality
9
- export { patchOpenAI, patchOpenAIInstance, getProviderInfo } from './instance-patcher.js';
@@ -1,288 +0,0 @@
1
- /**
2
- * Instance Patcher Module
3
- *
4
- * Handles patching of OpenAI client instances.
5
- * Extracted from wrapper.ts for better organization.
6
- */
7
-
8
- import OpenAI from 'openai';
9
- import type { ChatCompletionCreateParams } from 'openai/resources/chat/completions';
10
- import type { EmbeddingCreateParams } from 'openai/resources/embeddings';
11
- import { ProviderInfo } from '../../types/index.js';
12
- import {
13
- OpenAIClientInstance,
14
- OpenAIChatRequest,
15
- OpenAIEmbeddingRequest,
16
- OpenAIRequestOptions,
17
- OpenAIResponsesOriginalFunction,
18
- } from '../../types/function-parameters.js';
19
- import { isOpenAIClientInstance } from '../../utils/type-guards.js';
20
- import { extractMetadata, createLoggingContext } from '../../utils/metadata-builder.js';
21
- import {
22
- requestHandlerFactory,
23
- ensureFactoryInitialized,
24
- } from '../../utils/request-handler-factory.js';
25
- import { getLogger, getConfig } from '../config/index.js';
26
- import { detectProvider } from '../providers/index.js';
27
- import { OpenAIResponsesRequest } from '../../types/responses-api.js';
28
-
29
- // Import the type augmentations to ensure they're available
30
- import '../../types/openai-augmentation.js';
31
-
32
- // Interface for OpenAI instance with Responses API
33
- interface OpenAIWithResponses extends OpenAIClientInstance {
34
- responses?: {
35
- create: (params: OpenAIResponsesRequest, options?: OpenAIRequestOptions) => Promise<unknown>;
36
- };
37
- }
38
-
39
- /**
40
- * Set to track patched instances
41
- */
42
- const patchedInstances = new WeakSet();
43
-
44
- /**
45
- * WeakMap to store provider information for each client instance
46
- */
47
- const instanceProviders = new WeakMap<OpenAIClientInstance, ProviderInfo>();
48
-
49
- // Global logger
50
- const logger = getLogger();
51
-
52
- /**
53
- * Get provider information for a client instance
54
- */
55
- export function getProviderInfo(instance: OpenAIClientInstance): ProviderInfo | undefined {
56
- return instanceProviders.get(instance);
57
- }
58
-
59
- /**
60
- * Simple approach: Only patch instances when users create them
61
- * No global patching, no dummy keys - just clean instance patching
62
- */
63
- export function patchOpenAI(): void {
64
- logger.info('Revenium OpenAI middleware loaded and ready');
65
- logger.debug('Use patchOpenAIInstance() to patch specific OpenAI instances');
66
- }
67
-
68
- /**
69
- * Manually patch an existing OpenAI instance
70
- * This is the main function users should call
71
- */
72
- export function patchOpenAIInstance(instance: OpenAI): OpenAI {
73
- // Check if middleware is initialized
74
- const config = getConfig();
75
- if (!config) {
76
- logger.warn('Revenium middleware not initialized.');
77
- logger.warn(
78
- 'Auto-initialization may have failed. Try calling initializeReveniumFromEnv() explicitly.'
79
- );
80
- logger.warn('Check that REVENIUM_METERING_API_KEY environment variable is set.');
81
- logger.warn(
82
- 'OpenAI instance will be patched but tracking may not work without proper configuration.'
83
- );
84
- } else {
85
- logger.debug('Revenium middleware is properly configured');
86
- }
87
-
88
- if (patchedInstances.has(instance)) {
89
- logger.debug('OpenAI instance already patched, skipping');
90
- return instance;
91
- }
92
-
93
- patchInstance(instance as unknown as OpenAIClientInstance);
94
- logger.debug('OpenAI instance patched successfully');
95
-
96
- return instance;
97
- }
98
-
99
- /**
100
- * Patch an individual OpenAI instance
101
- */
102
- function patchInstance(instance: OpenAIClientInstance): void {
103
- try {
104
- // Validate instance
105
- if (!isOpenAIClientInstance(instance)) {
106
- logger.error('Invalid OpenAI client instance provided to patchInstance');
107
- return;
108
- }
109
-
110
- // Detect provider type for this instance
111
- const providerInfo = detectProvider(instance);
112
- instanceProviders.set(instance, providerInfo);
113
-
114
- logger.debug('Provider detection completed for instance', {
115
- provider: providerInfo.provider,
116
- isAzure: providerInfo.isAzure,
117
- hasAzureConfig: !!providerInfo.azureConfig,
118
- });
119
-
120
- // Patch chat completions
121
- patchChatCompletions(instance);
122
-
123
- // Patch embeddings
124
- patchEmbeddings(instance);
125
-
126
- // Patch responses API (new OpenAI Responses API)
127
- patchResponses(instance);
128
-
129
- // Mark as patched
130
- patchedInstances.add(instance);
131
- } catch (error) {
132
- logger.error('Failed to patch OpenAI instance', {
133
- error: error instanceof Error ? error.message : String(error),
134
- });
135
- }
136
- }
137
-
138
- /**
139
- * Patch chat completions endpoint
140
- */
141
- function patchChatCompletions(instance: OpenAIClientInstance): void {
142
- if (!instance.chat || !instance.chat.completions || !instance.chat.completions.create) {
143
- return logger.warn('OpenAI instance missing chat.completions.create, skipping chat patch');
144
- }
145
-
146
- // Store the original create method
147
- const originalCreate = instance.chat.completions.create.bind(instance.chat.completions);
148
-
149
- // Replace the create method with our wrapped version
150
- instance.chat.completions.create = async function (
151
- params: ChatCompletionCreateParams,
152
- options?: OpenAIRequestOptions
153
- ) {
154
- // Extract metadata using utility
155
- const { metadata, cleanParams } = extractMetadata(params as OpenAIChatRequest);
156
- const typedParams = params as OpenAIChatRequest;
157
-
158
- logger.debug('OpenAI chat.completions.create intercepted', {
159
- ...createLoggingContext(metadata),
160
- model: typedParams.model,
161
- stream: !!typedParams.stream,
162
- });
163
-
164
- // Record request start time
165
- const requestStartTime = Date.now();
166
-
167
- // Ensure factory is initialized and route request
168
- try {
169
- await ensureFactoryInitialized();
170
- } catch (error) {
171
- logger.error('Failed to initialize request handler factory', { error });
172
- throw new Error('Middleware initialization failed - cannot process request');
173
- }
174
- return requestHandlerFactory.routeChatRequest(
175
- originalCreate,
176
- cleanParams,
177
- options,
178
- metadata,
179
- requestStartTime,
180
- instance
181
- );
182
- };
183
- }
184
-
185
- /**
186
- * Patch embeddings endpoint
187
- */
188
- function patchEmbeddings(instance: OpenAIClientInstance): void {
189
- if (!instance.embeddings || !instance.embeddings.create) {
190
- return logger.warn('OpenAI instance missing embeddings.create, skipping embeddings patch');
191
- }
192
- // Store the original embeddings create method
193
- const originalEmbeddingsCreate = instance.embeddings.create.bind(instance.embeddings);
194
-
195
- // Replace the embeddings create method with our wrapped version
196
- instance.embeddings.create = async function (
197
- params: EmbeddingCreateParams,
198
- options?: OpenAIRequestOptions
199
- ) {
200
- // Extract metadata using utility
201
- const { metadata, cleanParams } = extractMetadata(params as OpenAIEmbeddingRequest);
202
- const typedParams = params as OpenAIEmbeddingRequest;
203
-
204
- logger.debug('OpenAI embeddings.create intercepted', {
205
- ...createLoggingContext(metadata),
206
- model: typedParams.model,
207
- inputType: typeof typedParams.input,
208
- });
209
-
210
- // Record request start time
211
- const requestStartTime = Date.now();
212
-
213
- // Ensure factory is initialized and route request
214
- try {
215
- await ensureFactoryInitialized();
216
- } catch (error) {
217
- logger.error('Failed to initialize request handler factory', { error });
218
- throw new Error('Middleware initialization failed - cannot process request');
219
- }
220
- return requestHandlerFactory.routeEmbeddingsRequest(
221
- originalEmbeddingsCreate,
222
- cleanParams,
223
- options,
224
- metadata,
225
- requestStartTime,
226
- instance
227
- );
228
- };
229
- }
230
-
231
- /**
232
- * Patch responses endpoint (new OpenAI Responses API)
233
- */
234
- function patchResponses(instance: OpenAIClientInstance): void {
235
- // Type assertion for new Responses API (not yet in OpenAI types)
236
- const responsesAPI = instance as OpenAIWithResponses;
237
-
238
- // Check if the instance has the responses API (it's a newer feature)
239
- if (!responsesAPI.responses || !responsesAPI.responses.create) {
240
- logger.debug(
241
- 'OpenAI instance missing responses.create, skipping responses patch (this is normal for older SDK versions)'
242
- );
243
- return;
244
- }
245
-
246
- // Store the original responses create method
247
- const originalResponsesCreate = responsesAPI.responses.create.bind(responsesAPI.responses);
248
-
249
- // Replace the responses create method with our wrapped version
250
- responsesAPI.responses.create = async function (
251
- params: OpenAIResponsesRequest,
252
- options?: OpenAIRequestOptions
253
- ) {
254
- // Extract metadata using utility (similar to chat completions)
255
- const { metadata, cleanParams } = extractMetadata(params);
256
-
257
- logger.debug('OpenAI responses.create intercepted', {
258
- ...createLoggingContext(metadata),
259
- model: params.model,
260
- stream: !!params.stream,
261
- inputType: typeof params.input,
262
- });
263
-
264
- // Record request start time
265
- const requestStartTime = Date.now();
266
-
267
- // Ensure factory is initialized and route request
268
- try {
269
- await ensureFactoryInitialized();
270
- } catch (error) {
271
- logger.error('Failed to initialize request handler factory', { error });
272
- throw new Error('Middleware initialization failed - cannot process request');
273
- }
274
- return requestHandlerFactory.routeResponsesRequest(
275
- originalResponsesCreate as OpenAIResponsesOriginalFunction,
276
- cleanParams,
277
- options,
278
- metadata,
279
- requestStartTime,
280
- instance
281
- );
282
- };
283
- }
284
-
285
- /**
286
- * Export instance providers for request handlers
287
- */
288
- export { instanceProviders };