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