@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,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
- }