@revenium/openai 1.0.13 → 1.0.15

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 (240) hide show
  1. package/.env.example +10 -15
  2. package/CHANGELOG.md +65 -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 +21 -0
  22. package/dist/cjs/core/middleware/index.js.map +1 -0
  23. package/dist/cjs/core/middleware/interfaces.js +454 -0
  24. package/dist/cjs/core/middleware/interfaces.js.map +1 -0
  25. package/dist/cjs/core/middleware/revenium-client.js +152 -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 +21 -14
  32. package/dist/cjs/core/tracking/api-client.js.map +1 -1
  33. package/dist/cjs/core/tracking/index.js +5 -1
  34. package/dist/cjs/core/tracking/index.js.map +1 -1
  35. package/dist/cjs/core/tracking/payload-builder.js +143 -25
  36. package/dist/cjs/core/tracking/payload-builder.js.map +1 -1
  37. package/dist/cjs/core/tracking/usage-tracker.js +111 -18
  38. package/dist/cjs/core/tracking/usage-tracker.js.map +1 -1
  39. package/dist/cjs/index.js +39 -202
  40. package/dist/cjs/index.js.map +1 -1
  41. package/dist/cjs/types/index.js +0 -8
  42. package/dist/cjs/types/index.js.map +1 -1
  43. package/dist/cjs/types/openai-augmentation.js +0 -49
  44. package/dist/cjs/types/openai-augmentation.js.map +1 -1
  45. package/dist/cjs/utils/constants.js +17 -20
  46. package/dist/cjs/utils/constants.js.map +1 -1
  47. package/dist/cjs/utils/error-handler.js +18 -14
  48. package/dist/cjs/utils/error-handler.js.map +1 -1
  49. package/dist/cjs/utils/metadata-builder.js +17 -16
  50. package/dist/cjs/utils/metadata-builder.js.map +1 -1
  51. package/dist/cjs/utils/provider-detection.js +25 -28
  52. package/dist/cjs/utils/provider-detection.js.map +1 -1
  53. package/dist/cjs/utils/trace-fields.js +115 -0
  54. package/dist/cjs/utils/trace-fields.js.map +1 -0
  55. package/dist/esm/core/client/index.js +6 -0
  56. package/dist/esm/core/client/index.js.map +1 -0
  57. package/dist/esm/core/client/manager.js +102 -0
  58. package/dist/esm/core/client/manager.js.map +1 -0
  59. package/dist/esm/core/config/azure-config.js +6 -18
  60. package/dist/esm/core/config/azure-config.js.map +1 -1
  61. package/dist/esm/core/config/index.js +5 -4
  62. package/dist/esm/core/config/index.js.map +1 -1
  63. package/dist/esm/core/config/loader.js +33 -13
  64. package/dist/esm/core/config/loader.js.map +1 -1
  65. package/dist/esm/core/config/manager.js +13 -7
  66. package/dist/esm/core/config/manager.js.map +1 -1
  67. package/dist/esm/core/config/validator.js +3 -44
  68. package/dist/esm/core/config/validator.js.map +1 -1
  69. package/dist/esm/core/middleware/index.js +8 -0
  70. package/dist/esm/core/middleware/index.js.map +1 -0
  71. package/dist/esm/core/middleware/interfaces.js +442 -0
  72. package/dist/esm/core/middleware/interfaces.js.map +1 -0
  73. package/dist/esm/core/middleware/revenium-client.js +115 -0
  74. package/dist/esm/core/middleware/revenium-client.js.map +1 -0
  75. package/dist/esm/core/providers/detector.js +43 -22
  76. package/dist/esm/core/providers/detector.js.map +1 -1
  77. package/dist/esm/core/providers/index.js +2 -2
  78. package/dist/esm/core/providers/index.js.map +1 -1
  79. package/dist/esm/core/tracking/api-client.js +20 -13
  80. package/dist/esm/core/tracking/api-client.js.map +1 -1
  81. package/dist/esm/core/tracking/index.js +4 -4
  82. package/dist/esm/core/tracking/index.js.map +1 -1
  83. package/dist/esm/core/tracking/payload-builder.js +142 -26
  84. package/dist/esm/core/tracking/payload-builder.js.map +1 -1
  85. package/dist/esm/core/tracking/usage-tracker.js +78 -20
  86. package/dist/esm/core/tracking/usage-tracker.js.map +1 -1
  87. package/dist/esm/index.js +9 -177
  88. package/dist/esm/index.js.map +1 -1
  89. package/dist/esm/types/index.js +2 -10
  90. package/dist/esm/types/index.js.map +1 -1
  91. package/dist/esm/types/openai-augmentation.js +0 -49
  92. package/dist/esm/types/openai-augmentation.js.map +1 -1
  93. package/dist/esm/utils/constants.js +16 -19
  94. package/dist/esm/utils/constants.js.map +1 -1
  95. package/dist/esm/utils/error-handler.js +19 -15
  96. package/dist/esm/utils/error-handler.js.map +1 -1
  97. package/dist/esm/utils/metadata-builder.js +17 -16
  98. package/dist/esm/utils/metadata-builder.js.map +1 -1
  99. package/dist/esm/utils/provider-detection.js +26 -29
  100. package/dist/esm/utils/provider-detection.js.map +1 -1
  101. package/dist/esm/utils/trace-fields.js +100 -0
  102. package/dist/esm/utils/trace-fields.js.map +1 -0
  103. package/dist/types/core/client/index.d.ts +6 -0
  104. package/dist/types/core/client/index.d.ts.map +1 -0
  105. package/dist/types/core/client/manager.d.ts +32 -0
  106. package/dist/types/core/client/manager.d.ts.map +1 -0
  107. package/dist/types/core/config/azure-config.d.ts +2 -2
  108. package/dist/types/core/config/azure-config.d.ts.map +1 -1
  109. package/dist/types/core/config/index.d.ts +4 -4
  110. package/dist/types/core/config/index.d.ts.map +1 -1
  111. package/dist/types/core/config/loader.d.ts +3 -1
  112. package/dist/types/core/config/loader.d.ts.map +1 -1
  113. package/dist/types/core/config/manager.d.ts +1 -1
  114. package/dist/types/core/config/manager.d.ts.map +1 -1
  115. package/dist/types/core/config/validator.d.ts +1 -12
  116. package/dist/types/core/config/validator.d.ts.map +1 -1
  117. package/dist/types/core/middleware/index.d.ts +8 -0
  118. package/dist/types/core/middleware/index.d.ts.map +1 -0
  119. package/dist/types/core/middleware/interfaces.d.ts +104 -0
  120. package/dist/types/core/middleware/interfaces.d.ts.map +1 -0
  121. package/dist/types/core/middleware/revenium-client.d.ts +64 -0
  122. package/dist/types/core/middleware/revenium-client.d.ts.map +1 -0
  123. package/dist/types/core/providers/detector.d.ts +9 -2
  124. package/dist/types/core/providers/detector.d.ts.map +1 -1
  125. package/dist/types/core/providers/index.d.ts +2 -2
  126. package/dist/types/core/providers/index.d.ts.map +1 -1
  127. package/dist/types/core/tracking/api-client.d.ts +1 -1
  128. package/dist/types/core/tracking/api-client.d.ts.map +1 -1
  129. package/dist/types/core/tracking/index.d.ts +4 -4
  130. package/dist/types/core/tracking/index.d.ts.map +1 -1
  131. package/dist/types/core/tracking/payload-builder.d.ts +5 -3
  132. package/dist/types/core/tracking/payload-builder.d.ts.map +1 -1
  133. package/dist/types/core/tracking/usage-tracker.d.ts +4 -2
  134. package/dist/types/core/tracking/usage-tracker.d.ts.map +1 -1
  135. package/dist/types/index.d.ts +11 -135
  136. package/dist/types/index.d.ts.map +1 -1
  137. package/dist/types/types/function-parameters.d.ts +91 -23
  138. package/dist/types/types/function-parameters.d.ts.map +1 -1
  139. package/dist/types/types/index.d.ts +53 -108
  140. package/dist/types/types/index.d.ts.map +1 -1
  141. package/dist/types/types/openai-augmentation.d.ts +4 -138
  142. package/dist/types/types/openai-augmentation.d.ts.map +1 -1
  143. package/dist/types/utils/constants.d.ts +7 -1
  144. package/dist/types/utils/constants.d.ts.map +1 -1
  145. package/dist/types/utils/error-handler.d.ts +2 -2
  146. package/dist/types/utils/error-handler.d.ts.map +1 -1
  147. package/dist/types/utils/metadata-builder.d.ts +2 -2
  148. package/dist/types/utils/metadata-builder.d.ts.map +1 -1
  149. package/dist/types/utils/provider-detection.d.ts +3 -3
  150. package/dist/types/utils/provider-detection.d.ts.map +1 -1
  151. package/dist/types/utils/trace-fields.d.ts +11 -0
  152. package/dist/types/utils/trace-fields.d.ts.map +1 -0
  153. package/examples/README.md +282 -198
  154. package/examples/azure/basic.ts +62 -0
  155. package/examples/azure/responses-basic.ts +45 -0
  156. package/examples/azure/responses-stream.ts +61 -0
  157. package/examples/azure/stream.ts +56 -0
  158. package/examples/getting_started.ts +31 -43
  159. package/examples/openai/basic.ts +45 -0
  160. package/examples/openai/metadata.ts +67 -0
  161. package/examples/openai/responses-basic.ts +44 -0
  162. package/examples/openai/responses-embed.ts +34 -0
  163. package/examples/openai/responses-streaming.ts +63 -0
  164. package/examples/openai/streaming.ts +59 -0
  165. package/package.json +23 -13
  166. package/dist/cjs/core/wrapper/index.js +0 -15
  167. package/dist/cjs/core/wrapper/index.js.map +0 -1
  168. package/dist/cjs/core/wrapper/instance-patcher.js +0 -202
  169. package/dist/cjs/core/wrapper/instance-patcher.js.map +0 -1
  170. package/dist/cjs/core/wrapper/request-handler.js +0 -317
  171. package/dist/cjs/core/wrapper/request-handler.js.map +0 -1
  172. package/dist/cjs/core/wrapper/stream-wrapper.js +0 -82
  173. package/dist/cjs/core/wrapper/stream-wrapper.js.map +0 -1
  174. package/dist/cjs/utils/azure-model-resolver.js +0 -211
  175. package/dist/cjs/utils/azure-model-resolver.js.map +0 -1
  176. package/dist/cjs/utils/request-handler-factory.js +0 -185
  177. package/dist/cjs/utils/request-handler-factory.js.map +0 -1
  178. package/dist/esm/core/wrapper/index.js +0 -9
  179. package/dist/esm/core/wrapper/index.js.map +0 -1
  180. package/dist/esm/core/wrapper/instance-patcher.js +0 -199
  181. package/dist/esm/core/wrapper/instance-patcher.js.map +0 -1
  182. package/dist/esm/core/wrapper/request-handler.js +0 -310
  183. package/dist/esm/core/wrapper/request-handler.js.map +0 -1
  184. package/dist/esm/core/wrapper/stream-wrapper.js +0 -79
  185. package/dist/esm/core/wrapper/stream-wrapper.js.map +0 -1
  186. package/dist/esm/utils/azure-model-resolver.js +0 -204
  187. package/dist/esm/utils/azure-model-resolver.js.map +0 -1
  188. package/dist/esm/utils/request-handler-factory.js +0 -146
  189. package/dist/esm/utils/request-handler-factory.js.map +0 -1
  190. package/dist/types/core/wrapper/index.d.ts +0 -8
  191. package/dist/types/core/wrapper/index.d.ts.map +0 -1
  192. package/dist/types/core/wrapper/instance-patcher.d.ts +0 -33
  193. package/dist/types/core/wrapper/instance-patcher.d.ts.map +0 -1
  194. package/dist/types/core/wrapper/request-handler.d.ts +0 -29
  195. package/dist/types/core/wrapper/request-handler.d.ts.map +0 -1
  196. package/dist/types/core/wrapper/stream-wrapper.d.ts +0 -13
  197. package/dist/types/core/wrapper/stream-wrapper.d.ts.map +0 -1
  198. package/dist/types/utils/azure-model-resolver.d.ts +0 -41
  199. package/dist/types/utils/azure-model-resolver.d.ts.map +0 -1
  200. package/dist/types/utils/request-handler-factory.d.ts +0 -81
  201. package/dist/types/utils/request-handler-factory.d.ts.map +0 -1
  202. package/examples/azure-basic.ts +0 -206
  203. package/examples/azure-responses-basic.ts +0 -233
  204. package/examples/azure-responses-streaming.ts +0 -255
  205. package/examples/azure-streaming.ts +0 -209
  206. package/examples/openai-basic.ts +0 -147
  207. package/examples/openai-function-calling.ts +0 -259
  208. package/examples/openai-responses-basic.ts +0 -212
  209. package/examples/openai-responses-streaming.ts +0 -232
  210. package/examples/openai-streaming.ts +0 -172
  211. package/examples/openai-vision.ts +0 -289
  212. package/src/core/config/azure-config.ts +0 -72
  213. package/src/core/config/index.ts +0 -23
  214. package/src/core/config/loader.ts +0 -66
  215. package/src/core/config/manager.ts +0 -95
  216. package/src/core/config/validator.ts +0 -89
  217. package/src/core/providers/detector.ts +0 -159
  218. package/src/core/providers/index.ts +0 -16
  219. package/src/core/tracking/api-client.ts +0 -78
  220. package/src/core/tracking/index.ts +0 -21
  221. package/src/core/tracking/payload-builder.ts +0 -137
  222. package/src/core/tracking/usage-tracker.ts +0 -189
  223. package/src/core/wrapper/index.ts +0 -9
  224. package/src/core/wrapper/instance-patcher.ts +0 -288
  225. package/src/core/wrapper/request-handler.ts +0 -423
  226. package/src/core/wrapper/stream-wrapper.ts +0 -100
  227. package/src/index.ts +0 -360
  228. package/src/types/function-parameters.ts +0 -251
  229. package/src/types/index.ts +0 -310
  230. package/src/types/openai-augmentation.ts +0 -232
  231. package/src/types/responses-api.ts +0 -308
  232. package/src/utils/azure-model-resolver.ts +0 -220
  233. package/src/utils/constants.ts +0 -21
  234. package/src/utils/error-handler.ts +0 -251
  235. package/src/utils/metadata-builder.ts +0 -228
  236. package/src/utils/provider-detection.ts +0 -257
  237. package/src/utils/request-handler-factory.ts +0 -285
  238. package/src/utils/stop-reason-mapper.ts +0 -78
  239. package/src/utils/type-guards.ts +0 -202
  240. 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
- }