@revenium/openai 1.0.13 → 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 (228) hide show
  1. package/.env.example +10 -15
  2. package/CHANGELOG.md +44 -11
  3. package/CODE_OF_CONDUCT.md +57 -0
  4. package/CONTRIBUTING.md +38 -0
  5. package/README.md +104 -216
  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 +11 -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 +15 -25
  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 -195
  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 +17 -16
  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/esm/core/client/index.js +6 -0
  52. package/dist/esm/core/client/index.js.map +1 -0
  53. package/dist/esm/core/client/manager.js +102 -0
  54. package/dist/esm/core/client/manager.js.map +1 -0
  55. package/dist/esm/core/config/azure-config.js +6 -18
  56. package/dist/esm/core/config/azure-config.js.map +1 -1
  57. package/dist/esm/core/config/index.js +5 -4
  58. package/dist/esm/core/config/index.js.map +1 -1
  59. package/dist/esm/core/config/loader.js +33 -13
  60. package/dist/esm/core/config/loader.js.map +1 -1
  61. package/dist/esm/core/config/manager.js +13 -7
  62. package/dist/esm/core/config/manager.js.map +1 -1
  63. package/dist/esm/core/config/validator.js +3 -44
  64. package/dist/esm/core/config/validator.js.map +1 -1
  65. package/dist/esm/core/middleware/index.js +8 -0
  66. package/dist/esm/core/middleware/index.js.map +1 -0
  67. package/dist/esm/core/middleware/interfaces.js +353 -0
  68. package/dist/esm/core/middleware/interfaces.js.map +1 -0
  69. package/dist/esm/core/middleware/revenium-client.js +105 -0
  70. package/dist/esm/core/middleware/revenium-client.js.map +1 -0
  71. package/dist/esm/core/providers/detector.js +43 -22
  72. package/dist/esm/core/providers/detector.js.map +1 -1
  73. package/dist/esm/core/providers/index.js +2 -2
  74. package/dist/esm/core/providers/index.js.map +1 -1
  75. package/dist/esm/core/tracking/api-client.js +13 -12
  76. package/dist/esm/core/tracking/api-client.js.map +1 -1
  77. package/dist/esm/core/tracking/payload-builder.js +16 -26
  78. package/dist/esm/core/tracking/payload-builder.js.map +1 -1
  79. package/dist/esm/core/tracking/usage-tracker.js +24 -20
  80. package/dist/esm/core/tracking/usage-tracker.js.map +1 -1
  81. package/dist/esm/index.js +9 -177
  82. package/dist/esm/index.js.map +1 -1
  83. package/dist/esm/types/index.js +2 -10
  84. package/dist/esm/types/index.js.map +1 -1
  85. package/dist/esm/types/openai-augmentation.js +0 -49
  86. package/dist/esm/types/openai-augmentation.js.map +1 -1
  87. package/dist/esm/utils/constants.js +16 -19
  88. package/dist/esm/utils/constants.js.map +1 -1
  89. package/dist/esm/utils/error-handler.js +19 -15
  90. package/dist/esm/utils/error-handler.js.map +1 -1
  91. package/dist/esm/utils/metadata-builder.js +17 -16
  92. package/dist/esm/utils/metadata-builder.js.map +1 -1
  93. package/dist/esm/utils/provider-detection.js +26 -29
  94. package/dist/esm/utils/provider-detection.js.map +1 -1
  95. package/dist/types/core/client/index.d.ts +6 -0
  96. package/dist/types/core/client/index.d.ts.map +1 -0
  97. package/dist/types/core/client/manager.d.ts +32 -0
  98. package/dist/types/core/client/manager.d.ts.map +1 -0
  99. package/dist/types/core/config/azure-config.d.ts +2 -2
  100. package/dist/types/core/config/azure-config.d.ts.map +1 -1
  101. package/dist/types/core/config/index.d.ts +4 -4
  102. package/dist/types/core/config/index.d.ts.map +1 -1
  103. package/dist/types/core/config/loader.d.ts +3 -1
  104. package/dist/types/core/config/loader.d.ts.map +1 -1
  105. package/dist/types/core/config/manager.d.ts +1 -1
  106. package/dist/types/core/config/manager.d.ts.map +1 -1
  107. package/dist/types/core/config/validator.d.ts +1 -12
  108. package/dist/types/core/config/validator.d.ts.map +1 -1
  109. package/dist/types/core/middleware/index.d.ts +8 -0
  110. package/dist/types/core/middleware/index.d.ts.map +1 -0
  111. package/dist/types/core/middleware/interfaces.d.ts +74 -0
  112. package/dist/types/core/middleware/interfaces.d.ts.map +1 -0
  113. package/dist/types/core/middleware/revenium-client.d.ts +58 -0
  114. package/dist/types/core/middleware/revenium-client.d.ts.map +1 -0
  115. package/dist/types/core/providers/detector.d.ts +9 -2
  116. package/dist/types/core/providers/detector.d.ts.map +1 -1
  117. package/dist/types/core/providers/index.d.ts +2 -2
  118. package/dist/types/core/providers/index.d.ts.map +1 -1
  119. package/dist/types/core/tracking/api-client.d.ts +1 -1
  120. package/dist/types/core/tracking/api-client.d.ts.map +1 -1
  121. package/dist/types/core/tracking/payload-builder.d.ts +3 -3
  122. package/dist/types/core/tracking/payload-builder.d.ts.map +1 -1
  123. package/dist/types/core/tracking/usage-tracker.d.ts +2 -2
  124. package/dist/types/core/tracking/usage-tracker.d.ts.map +1 -1
  125. package/dist/types/index.d.ts +11 -135
  126. package/dist/types/index.d.ts.map +1 -1
  127. package/dist/types/types/function-parameters.d.ts +2 -23
  128. package/dist/types/types/function-parameters.d.ts.map +1 -1
  129. package/dist/types/types/index.d.ts +11 -105
  130. package/dist/types/types/index.d.ts.map +1 -1
  131. package/dist/types/types/openai-augmentation.d.ts +4 -138
  132. package/dist/types/types/openai-augmentation.d.ts.map +1 -1
  133. package/dist/types/utils/constants.d.ts +7 -1
  134. package/dist/types/utils/constants.d.ts.map +1 -1
  135. package/dist/types/utils/error-handler.d.ts +2 -2
  136. package/dist/types/utils/error-handler.d.ts.map +1 -1
  137. package/dist/types/utils/metadata-builder.d.ts +2 -2
  138. package/dist/types/utils/metadata-builder.d.ts.map +1 -1
  139. package/dist/types/utils/provider-detection.d.ts +3 -3
  140. package/dist/types/utils/provider-detection.d.ts.map +1 -1
  141. package/examples/README.md +282 -198
  142. package/examples/azure/basic.ts +62 -0
  143. package/examples/azure/responses-basic.ts +45 -0
  144. package/examples/azure/responses-stream.ts +61 -0
  145. package/examples/azure/stream.ts +56 -0
  146. package/examples/getting_started.ts +31 -43
  147. package/examples/openai/basic.ts +45 -0
  148. package/examples/openai/metadata.ts +67 -0
  149. package/examples/openai/responses-basic.ts +44 -0
  150. package/examples/openai/responses-embed.ts +34 -0
  151. package/examples/openai/responses-streaming.ts +63 -0
  152. package/examples/openai/streaming.ts +59 -0
  153. package/package.json +20 -13
  154. package/dist/cjs/core/wrapper/index.js +0 -15
  155. package/dist/cjs/core/wrapper/index.js.map +0 -1
  156. package/dist/cjs/core/wrapper/instance-patcher.js +0 -202
  157. package/dist/cjs/core/wrapper/instance-patcher.js.map +0 -1
  158. package/dist/cjs/core/wrapper/request-handler.js +0 -317
  159. package/dist/cjs/core/wrapper/request-handler.js.map +0 -1
  160. package/dist/cjs/core/wrapper/stream-wrapper.js +0 -82
  161. package/dist/cjs/core/wrapper/stream-wrapper.js.map +0 -1
  162. package/dist/cjs/utils/azure-model-resolver.js +0 -211
  163. package/dist/cjs/utils/azure-model-resolver.js.map +0 -1
  164. package/dist/cjs/utils/request-handler-factory.js +0 -185
  165. package/dist/cjs/utils/request-handler-factory.js.map +0 -1
  166. package/dist/esm/core/wrapper/index.js +0 -9
  167. package/dist/esm/core/wrapper/index.js.map +0 -1
  168. package/dist/esm/core/wrapper/instance-patcher.js +0 -199
  169. package/dist/esm/core/wrapper/instance-patcher.js.map +0 -1
  170. package/dist/esm/core/wrapper/request-handler.js +0 -310
  171. package/dist/esm/core/wrapper/request-handler.js.map +0 -1
  172. package/dist/esm/core/wrapper/stream-wrapper.js +0 -79
  173. package/dist/esm/core/wrapper/stream-wrapper.js.map +0 -1
  174. package/dist/esm/utils/azure-model-resolver.js +0 -204
  175. package/dist/esm/utils/azure-model-resolver.js.map +0 -1
  176. package/dist/esm/utils/request-handler-factory.js +0 -146
  177. package/dist/esm/utils/request-handler-factory.js.map +0 -1
  178. package/dist/types/core/wrapper/index.d.ts +0 -8
  179. package/dist/types/core/wrapper/index.d.ts.map +0 -1
  180. package/dist/types/core/wrapper/instance-patcher.d.ts +0 -33
  181. package/dist/types/core/wrapper/instance-patcher.d.ts.map +0 -1
  182. package/dist/types/core/wrapper/request-handler.d.ts +0 -29
  183. package/dist/types/core/wrapper/request-handler.d.ts.map +0 -1
  184. package/dist/types/core/wrapper/stream-wrapper.d.ts +0 -13
  185. package/dist/types/core/wrapper/stream-wrapper.d.ts.map +0 -1
  186. package/dist/types/utils/azure-model-resolver.d.ts +0 -41
  187. package/dist/types/utils/azure-model-resolver.d.ts.map +0 -1
  188. package/dist/types/utils/request-handler-factory.d.ts +0 -81
  189. package/dist/types/utils/request-handler-factory.d.ts.map +0 -1
  190. package/examples/azure-basic.ts +0 -206
  191. package/examples/azure-responses-basic.ts +0 -233
  192. package/examples/azure-responses-streaming.ts +0 -255
  193. package/examples/azure-streaming.ts +0 -209
  194. package/examples/openai-basic.ts +0 -147
  195. package/examples/openai-function-calling.ts +0 -259
  196. package/examples/openai-responses-basic.ts +0 -212
  197. package/examples/openai-responses-streaming.ts +0 -232
  198. package/examples/openai-streaming.ts +0 -172
  199. package/examples/openai-vision.ts +0 -289
  200. package/src/core/config/azure-config.ts +0 -72
  201. package/src/core/config/index.ts +0 -23
  202. package/src/core/config/loader.ts +0 -66
  203. package/src/core/config/manager.ts +0 -95
  204. package/src/core/config/validator.ts +0 -89
  205. package/src/core/providers/detector.ts +0 -159
  206. package/src/core/providers/index.ts +0 -16
  207. package/src/core/tracking/api-client.ts +0 -78
  208. package/src/core/tracking/index.ts +0 -21
  209. package/src/core/tracking/payload-builder.ts +0 -137
  210. package/src/core/tracking/usage-tracker.ts +0 -189
  211. package/src/core/wrapper/index.ts +0 -9
  212. package/src/core/wrapper/instance-patcher.ts +0 -288
  213. package/src/core/wrapper/request-handler.ts +0 -423
  214. package/src/core/wrapper/stream-wrapper.ts +0 -100
  215. package/src/index.ts +0 -360
  216. package/src/types/function-parameters.ts +0 -251
  217. package/src/types/index.ts +0 -310
  218. package/src/types/openai-augmentation.ts +0 -232
  219. package/src/types/responses-api.ts +0 -308
  220. package/src/utils/azure-model-resolver.ts +0 -220
  221. package/src/utils/constants.ts +0 -21
  222. package/src/utils/error-handler.ts +0 -251
  223. package/src/utils/metadata-builder.ts +0 -228
  224. package/src/utils/provider-detection.ts +0 -257
  225. package/src/utils/request-handler-factory.ts +0 -285
  226. package/src/utils/stop-reason-mapper.ts +0 -78
  227. package/src/utils/type-guards.ts +0 -202
  228. package/src/utils/url-builder.ts +0 -68
@@ -1,423 +0,0 @@
1
- /**
2
- * Request Handler Module
3
- *
4
- * Handles different types of OpenAI requests (streaming, non-streaming, embeddings).
5
- * Extracted from wrapper.ts for better organization.
6
- */
7
-
8
- import { randomUUID } from 'crypto';
9
- import { UsageMetadata, OpenAIResponsesRequest } from '../../types/index.js';
10
- import {
11
- OpenAIChatResponse,
12
- OpenAIEmbeddingResponse,
13
- OpenAIChatRequest,
14
- OpenAIEmbeddingRequest,
15
- OpenAIClientInstance,
16
- OpenAIRequestOptions,
17
- OpenAIOriginalFunction,
18
- OpenAIResponsesOriginalFunction,
19
- } from '../../types/function-parameters.js';
20
- import {
21
- isOpenAIChatResponse,
22
- isOpenAIEmbeddingResponse,
23
- hasValidUsage,
24
- } from '../../utils/type-guards.js';
25
- import { safeAsyncOperation, NetworkError, classifyError } from '../../utils/error-handler.js';
26
- import { createLoggingContext } from '../../utils/metadata-builder.js';
27
- import { trackUsageAsync, trackEmbeddingsUsageAsync } from '../tracking/index.js';
28
- import { getLogger } from '../config/index.js';
29
- import { instanceProviders } from './instance-patcher.js';
30
- import { createTrackingStreamWrapper } from './stream-wrapper.js';
31
-
32
- // Global logger
33
- const logger = getLogger();
34
-
35
- /**
36
- * Handle non-streaming OpenAI requests
37
- */
38
- export async function handleNonStreamingRequest(
39
- originalCreate: OpenAIOriginalFunction,
40
- params: Omit<OpenAIChatRequest, 'usageMetadata'> | Omit<OpenAIEmbeddingRequest, 'usageMetadata'>,
41
- options: OpenAIRequestOptions | undefined,
42
- usageMetadata: UsageMetadata | undefined,
43
- requestStartTime: number,
44
- instance: OpenAIClientInstance
45
- ): Promise<OpenAIChatResponse | OpenAIEmbeddingResponse> {
46
- const loggingContext = createLoggingContext(usageMetadata);
47
-
48
- const result = await safeAsyncOperation(
49
- async () => {
50
- // Call the original OpenAI method (cast params back to original type since usageMetadata is removed)
51
- const response = await originalCreate(params as any, options);
52
-
53
- // Validate response structure
54
- if (!hasValidUsage(response)) {
55
- logger.warn('Invalid response structure from OpenAI API', {
56
- ...loggingContext,
57
- response,
58
- });
59
- return response;
60
- }
61
-
62
- // Calculate duration
63
- const duration = Date.now() - requestStartTime;
64
-
65
- // Get provider info for this instance
66
- const providerInfo = instanceProviders.get(instance);
67
-
68
- // Track usage for chat completions
69
- if (isOpenAIChatResponse(response)) {
70
- trackUsageAsync({
71
- requestId: response.id,
72
- model: response.model,
73
- promptTokens: response.usage.prompt_tokens,
74
- completionTokens: response.usage.completion_tokens || 0,
75
- totalTokens: response.usage.total_tokens,
76
- reasoningTokens: response.usage.reasoning_tokens,
77
- cachedTokens: response.usage.cached_tokens,
78
- duration,
79
- finishReason: response.choices?.[0]?.finish_reason || null,
80
- usageMetadata,
81
- isStreamed: false,
82
- providerInfo,
83
- });
84
- }
85
-
86
- logger.debug('Chat completion request completed', {
87
- ...loggingContext,
88
- model: response.model,
89
- duration,
90
- totalTokens: response.usage.total_tokens,
91
- });
92
-
93
- return response;
94
- },
95
- 'Non-streaming OpenAI request',
96
- {
97
- logError: true,
98
- rethrow: true,
99
- messagePrefix: 'Chat completion request failed: ',
100
- transformError: error => {
101
- const classified = classifyError(error);
102
- if (classified.type === 'network') {
103
- return new NetworkError(classified.message, {
104
- ...loggingContext,
105
- duration: Date.now() - requestStartTime,
106
- });
107
- }
108
- return error instanceof Error ? error : new Error(String(error));
109
- },
110
- },
111
- logger
112
- );
113
-
114
- if (!result) throw new Error('OpenAI request failed without specific error');
115
- return result;
116
- }
117
-
118
- /**
119
- * Handle streaming OpenAI requests
120
- */
121
- export async function handleStreamingRequest(
122
- originalCreate: OpenAIOriginalFunction,
123
- params: Omit<OpenAIChatRequest, 'usageMetadata'>,
124
- options: OpenAIRequestOptions | undefined,
125
- usageMetadata: UsageMetadata | undefined,
126
- requestStartTime: number,
127
- instance: OpenAIClientInstance
128
- ): Promise<AsyncIterable<unknown>> {
129
- try {
130
- // Ensure stream_options includes usage data for token tracking
131
- const enhancedParams = {
132
- ...params,
133
- stream_options: {
134
- include_usage: true,
135
- ...(params.stream_options || {}),
136
- },
137
- };
138
-
139
- logger.debug('Enhanced streaming params with usage tracking', {
140
- originalStreamOptions: params.stream_options,
141
- enhancedStreamOptions: enhancedParams.stream_options,
142
- });
143
-
144
- // Call the original OpenAI method to get the stream (cast params back to original type since usageMetadata is removed)
145
- const originalStream = await originalCreate(enhancedParams as any, options);
146
-
147
- logger.debug('Chat completion streaming request initiated', {
148
- model: params.model,
149
- });
150
-
151
- // Return a wrapped stream that tracks usage when complete
152
- return createTrackingStreamWrapper(
153
- originalStream as unknown as AsyncIterable<unknown>,
154
- usageMetadata,
155
- requestStartTime,
156
- instance
157
- );
158
- } catch (error) {
159
- const duration = Date.now() - requestStartTime;
160
- logger.error('Chat completion streaming request failed', {
161
- error: error instanceof Error ? error.message : String(error),
162
- duration,
163
- });
164
-
165
- // Re-throw the error to maintain original behavior
166
- throw error;
167
- }
168
- }
169
-
170
- /**
171
- * Handle embeddings requests
172
- */
173
- export async function handleEmbeddingsRequest(
174
- originalCreate: OpenAIOriginalFunction,
175
- params: Omit<OpenAIEmbeddingRequest, 'usageMetadata'>,
176
- options: OpenAIRequestOptions | undefined,
177
- usageMetadata: UsageMetadata | undefined,
178
- requestStartTime: number,
179
- instance: OpenAIClientInstance
180
- ): Promise<OpenAIEmbeddingResponse> {
181
- try {
182
- // Call the original OpenAI method (cast params back to original type since usageMetadata is removed)
183
- const response = await originalCreate(params as any, options);
184
-
185
- // Validate response structure
186
- if (!isOpenAIEmbeddingResponse(response)) {
187
- logger.warn('Invalid embeddings response structure from OpenAI API', { response });
188
- return response as unknown as OpenAIEmbeddingResponse;
189
- }
190
-
191
- // Calculate duration
192
- const duration = Date.now() - requestStartTime;
193
-
194
- // Get provider info for this instance
195
- const providerInfo = instanceProviders.get(instance);
196
-
197
- // Track embeddings usage
198
- trackEmbeddingsUsageAsync({
199
- transactionId: `embed-${randomUUID()}`,
200
- model: response.model,
201
- promptTokens: response.usage.prompt_tokens,
202
- totalTokens: response.usage.total_tokens,
203
- duration,
204
- usageMetadata,
205
- requestStartTime,
206
- providerInfo,
207
- });
208
-
209
- logger.debug('Embeddings request completed', {
210
- model: response.model,
211
- duration,
212
- totalTokens: response.usage.total_tokens,
213
- });
214
-
215
- return response;
216
- } catch (error) {
217
- const duration = Date.now() - requestStartTime;
218
- logger.error('Embeddings request failed', {
219
- error: error instanceof Error ? error.message : String(error),
220
- duration,
221
- });
222
-
223
- // Re-throw the error to maintain original behavior
224
- throw error;
225
- }
226
- }
227
-
228
- /**
229
- * Handle non-streaming OpenAI Responses API requests
230
- */
231
- export async function handleResponsesNonStreamingRequest(
232
- originalCreate: OpenAIResponsesOriginalFunction,
233
- params: Omit<OpenAIResponsesRequest, 'usageMetadata'>,
234
- options: OpenAIRequestOptions | undefined,
235
- usageMetadata: UsageMetadata | undefined,
236
- requestStartTime: number,
237
- instance: OpenAIClientInstance
238
- ): Promise<unknown> {
239
- const loggingContext = createLoggingContext(usageMetadata);
240
-
241
- const result = await safeAsyncOperation(
242
- async () => {
243
- // Call the original OpenAI method (cast params back to original type since usageMetadata is removed)
244
- const response = await originalCreate(params as any, options);
245
-
246
- // Validate response structure
247
- if (!response || typeof response !== 'object') {
248
- throw new Error('Invalid response from OpenAI Responses API');
249
- }
250
-
251
- const duration = Date.now() - requestStartTime;
252
-
253
- // Extract usage information (Responses API may have different structure)
254
- const usage = (response as any).usage;
255
- if (usage) {
256
- // Track usage asynchronously using similar pattern to chat completions
257
- trackUsageAsync({
258
- requestId: (response as any).id || randomUUID(),
259
- model: (response as any).model || params.model,
260
- promptTokens: usage.input_tokens || 0,
261
- completionTokens: usage.output_tokens || 0,
262
- totalTokens: usage.total_tokens || 0,
263
- reasoningTokens: usage.reasoning_tokens,
264
- cachedTokens: usage.cached_tokens,
265
- duration,
266
- finishReason: (response as any).finish_reason || 'completed',
267
- usageMetadata,
268
- isStreamed: false,
269
- providerInfo: instanceProviders.get(instance),
270
- });
271
- }
272
-
273
- logger.debug('Responses API request completed', {
274
- ...loggingContext,
275
- model: (response as any).model,
276
- duration,
277
- totalTokens: usage?.total_tokens,
278
- });
279
-
280
- return response;
281
- },
282
- 'Non-streaming OpenAI Responses API request',
283
- {
284
- logError: true,
285
- rethrow: true,
286
- messagePrefix: 'Responses API request failed: ',
287
- transformError: error => {
288
- const classified = classifyError(error);
289
- if (classified.type === 'network') {
290
- return new NetworkError(classified.message, {
291
- ...loggingContext,
292
- duration: Date.now() - requestStartTime,
293
- });
294
- }
295
- return error instanceof Error ? error : new Error(String(error));
296
- },
297
- },
298
- logger
299
- );
300
-
301
- if (!result) throw new Error('OpenAI Responses API request failed without specific error');
302
- return result;
303
- }
304
-
305
- /**
306
- * Handle streaming OpenAI Responses API requests
307
- */
308
- export async function handleResponsesStreamingRequest(
309
- originalCreate: OpenAIResponsesOriginalFunction,
310
- params: Omit<OpenAIResponsesRequest, 'usageMetadata'>,
311
- options: OpenAIRequestOptions | undefined,
312
- usageMetadata: UsageMetadata | undefined,
313
- requestStartTime: number,
314
- instance: OpenAIClientInstance
315
- ): Promise<AsyncIterable<unknown>> {
316
- try {
317
- // Call the original OpenAI method to get the stream (cast params back to original type since usageMetadata is removed)
318
- const originalStream = await originalCreate(params as any, options);
319
-
320
- logger.debug('Responses API streaming request initiated', {
321
- model: params.model,
322
- });
323
-
324
- // Return a wrapped stream that tracks usage when complete
325
- // We'll use a similar pattern to chat completions but adapted for Responses API
326
- return createResponsesTrackingStreamWrapper(
327
- originalStream as unknown as AsyncIterable<unknown>,
328
- usageMetadata,
329
- requestStartTime,
330
- instance
331
- );
332
- } catch (error) {
333
- const duration = Date.now() - requestStartTime;
334
- logger.error('Responses API streaming request failed', {
335
- error: error instanceof Error ? error.message : String(error),
336
- duration,
337
- });
338
-
339
- // Re-throw the error to maintain original behavior
340
- throw error;
341
- }
342
- }
343
-
344
- /**
345
- * Create a tracking stream wrapper for Responses API
346
- * Similar to createTrackingStreamWrapper but adapted for Responses API structure
347
- */
348
- async function* createResponsesTrackingStreamWrapper(
349
- originalStream: AsyncIterable<unknown>,
350
- usageMetadata: UsageMetadata | undefined,
351
- requestStartTime: number,
352
- instance: OpenAIClientInstance
353
- ): AsyncIterable<unknown> {
354
- let firstChunkTime: number | undefined;
355
- let finalUsage: any = null;
356
- let model = '';
357
- let requestId = '';
358
- let finishReason: string | null = null;
359
-
360
- try {
361
- for await (const chunk of originalStream) {
362
- // Record time to first token
363
- if (!firstChunkTime) {
364
- firstChunkTime = Date.now();
365
- }
366
-
367
- // Extract information from chunk (Responses API structure may differ)
368
- if (chunk && typeof chunk === 'object') {
369
- const chunkObj = chunk as any;
370
-
371
- // Extract model and ID from chunk
372
- if (chunkObj.model) model = chunkObj.model;
373
- if (chunkObj.id) requestId = chunkObj.id;
374
-
375
- // Check for final usage information
376
- if (chunkObj.usage) {
377
- finalUsage = chunkObj.usage;
378
- }
379
-
380
- // Check for finish reason
381
- if (chunkObj.finish_reason) {
382
- finishReason = chunkObj.finish_reason;
383
- }
384
- }
385
-
386
- yield chunk;
387
- }
388
-
389
- // Track usage after stream completes
390
- if (finalUsage) {
391
- const duration = Date.now() - requestStartTime;
392
- const timeToFirstToken = firstChunkTime ? firstChunkTime - requestStartTime : undefined;
393
-
394
- trackUsageAsync({
395
- requestId: requestId || randomUUID(),
396
- model: model,
397
- promptTokens: finalUsage.input_tokens || 0,
398
- completionTokens: finalUsage.output_tokens || 0,
399
- totalTokens: finalUsage.total_tokens || 0,
400
- reasoningTokens: finalUsage.reasoning_tokens,
401
- cachedTokens: finalUsage.cached_tokens,
402
- duration,
403
- finishReason: finishReason || 'completed',
404
- usageMetadata,
405
- isStreamed: true,
406
- timeToFirstToken,
407
- providerInfo: instanceProviders.get(instance),
408
- });
409
-
410
- logger.debug('Responses API streaming completed', {
411
- model,
412
- duration,
413
- timeToFirstToken,
414
- totalTokens: finalUsage.total_tokens,
415
- });
416
- }
417
- } catch (error) {
418
- logger.error('Error in Responses API stream wrapper', {
419
- error: error instanceof Error ? error.message : String(error),
420
- });
421
- throw error;
422
- }
423
- }
@@ -1,100 +0,0 @@
1
- /**
2
- * Stream Wrapper Module
3
- *
4
- * Handles wrapping of streaming responses for usage tracking.
5
- * Extracted from wrapper.ts for better organization.
6
- */
7
-
8
- import { UsageMetadata } from '../../types/index.js';
9
- import {
10
- OpenAIClientInstance,
11
- StreamChunk,
12
- ExtendedUsage,
13
- } from '../../types/function-parameters.js';
14
- import { isStreamChunk } from '../../utils/type-guards.js';
15
- import { trackUsageAsync } from '../tracking/index.js';
16
- import { getLogger } from '../config/index.js';
17
- import { instanceProviders } from './instance-patcher.js';
18
-
19
- // Global logger
20
- const logger = getLogger();
21
-
22
- /**
23
- * Create a simple stream wrapper that tracks usage when complete
24
- */
25
- export function createTrackingStreamWrapper(
26
- originalStream: AsyncIterable<unknown>,
27
- usageMetadata: UsageMetadata | undefined,
28
- requestStartTime: number,
29
- instance: OpenAIClientInstance
30
- ): AsyncIterable<unknown> {
31
- // For streaming, we need to collect the final response data
32
- let accumulatedResponse: StreamChunk | null = null;
33
-
34
- // Create async iterator
35
- const wrappedIterator = {
36
- async *[Symbol.asyncIterator]() {
37
- try {
38
- for await (const chunk of originalStream) {
39
- // Validate and accumulate response data for tracking
40
- if (isStreamChunk(chunk)) {
41
- if (!accumulatedResponse) {
42
- accumulatedResponse = {
43
- id: chunk.id,
44
- model: chunk.model,
45
- usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
46
- };
47
- }
48
-
49
- // Update usage if available in chunk
50
- if (chunk.usage) {
51
- accumulatedResponse.usage = chunk.usage;
52
- }
53
- }
54
-
55
- // Forward the chunk to the client
56
- yield chunk;
57
- }
58
-
59
- // Stream completed - track usage
60
- if (accumulatedResponse && accumulatedResponse.usage) {
61
- const duration = Date.now() - requestStartTime;
62
-
63
- // Get provider info for this instance
64
- const providerInfo = instanceProviders.get(instance);
65
-
66
- // Safely access extended usage fields
67
- const usage = accumulatedResponse.usage as ExtendedUsage;
68
-
69
- trackUsageAsync({
70
- requestId: accumulatedResponse.id,
71
- model: accumulatedResponse.model,
72
- promptTokens: usage.prompt_tokens,
73
- completionTokens: usage.completion_tokens || 0,
74
- totalTokens: usage.total_tokens,
75
- reasoningTokens: usage.reasoning_tokens,
76
- cachedTokens: usage.cached_tokens,
77
- duration,
78
- finishReason: null, // Will be determined from final chunk
79
- usageMetadata,
80
- isStreamed: true,
81
- providerInfo,
82
- });
83
-
84
- logger.debug('Chat completion streaming completed', {
85
- model: accumulatedResponse.model,
86
- duration,
87
- totalTokens: accumulatedResponse.usage.total_tokens,
88
- });
89
- }
90
- } catch (error) {
91
- logger.error('Chat completion streaming error', {
92
- error: error instanceof Error ? error.message : String(error),
93
- });
94
- throw error;
95
- }
96
- },
97
- };
98
-
99
- return wrappedIterator;
100
- }