@revenium/perplexity 1.0.0
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/LICENSE +21 -0
- package/README.md +367 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/models/Exception/ApiResponseError.d.ts +6 -0
- package/dist/models/Exception/ApiResponseError.d.ts.map +1 -0
- package/dist/models/Exception/ApiResponseError.js +13 -0
- package/dist/models/Exception/ApiResponseError.js.map +1 -0
- package/dist/models/Exception/ConfigurationError.d.ts +4 -0
- package/dist/models/Exception/ConfigurationError.d.ts.map +1 -0
- package/dist/models/Exception/ConfigurationError.js +11 -0
- package/dist/models/Exception/ConfigurationError.js.map +1 -0
- package/dist/models/Exception/MeteringError.d.ts +5 -0
- package/dist/models/Exception/MeteringError.d.ts.map +1 -0
- package/dist/models/Exception/MeteringError.js +12 -0
- package/dist/models/Exception/MeteringError.js.map +1 -0
- package/dist/models/Exception/MiddlewareActivationError.d.ts +4 -0
- package/dist/models/Exception/MiddlewareActivationError.d.ts.map +1 -0
- package/dist/models/Exception/MiddlewareActivationError.js +11 -0
- package/dist/models/Exception/MiddlewareActivationError.js.map +1 -0
- package/dist/models/Exception/StreamTrackingError.d.ts +4 -0
- package/dist/models/Exception/StreamTrackingError.d.ts.map +1 -0
- package/dist/models/Exception/StreamTrackingError.js +11 -0
- package/dist/models/Exception/StreamTrackingError.js.map +1 -0
- package/dist/models/Exception/TokenCountingError.d.ts +4 -0
- package/dist/models/Exception/TokenCountingError.d.ts.map +1 -0
- package/dist/models/Exception/TokenCountingError.js +11 -0
- package/dist/models/Exception/TokenCountingError.js.map +1 -0
- package/dist/models/Exception/index.d.ts +7 -0
- package/dist/models/Exception/index.d.ts.map +1 -0
- package/dist/models/Exception/index.js +23 -0
- package/dist/models/Exception/index.js.map +1 -0
- package/dist/models/Logger.d.ts +10 -0
- package/dist/models/Logger.d.ts.map +1 -0
- package/dist/models/Logger.js +36 -0
- package/dist/models/Logger.js.map +1 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +19 -0
- package/dist/models/index.js.map +1 -0
- package/dist/perplexity-ai/client.d.ts +31 -0
- package/dist/perplexity-ai/client.d.ts.map +1 -0
- package/dist/perplexity-ai/client.js +75 -0
- package/dist/perplexity-ai/client.js.map +1 -0
- package/dist/perplexity-ai/index.d.ts +3 -0
- package/dist/perplexity-ai/index.d.ts.map +1 -0
- package/dist/perplexity-ai/index.js +19 -0
- package/dist/perplexity-ai/index.js.map +1 -0
- package/dist/perplexity-ai/middleware.d.ts +2 -0
- package/dist/perplexity-ai/middleware.d.ts.map +1 -0
- package/dist/perplexity-ai/middleware.js +163 -0
- package/dist/perplexity-ai/middleware.js.map +1 -0
- package/dist/types/context.d.ts +9 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +3 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +28 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/metering.d.ts +28 -0
- package/dist/types/metering.d.ts.map +1 -0
- package/dist/types/metering.js +3 -0
- package/dist/types/metering.js.map +1 -0
- package/dist/types/modelInfo.d.ts +6 -0
- package/dist/types/modelInfo.d.ts.map +1 -0
- package/dist/types/modelInfo.js +3 -0
- package/dist/types/modelInfo.js.map +1 -0
- package/dist/types/operation.d.ts +5 -0
- package/dist/types/operation.d.ts.map +1 -0
- package/dist/types/operation.js +9 -0
- package/dist/types/operation.js.map +1 -0
- package/dist/types/perplexityConfig.d.ts +5 -0
- package/dist/types/perplexityConfig.d.ts.map +1 -0
- package/dist/types/perplexityConfig.js +3 -0
- package/dist/types/perplexityConfig.js.map +1 -0
- package/dist/types/provider.d.ts +4 -0
- package/dist/types/provider.d.ts.map +1 -0
- package/dist/types/provider.js +8 -0
- package/dist/types/provider.js.map +1 -0
- package/dist/types/reveniumConfig.d.ts +6 -0
- package/dist/types/reveniumConfig.d.ts.map +1 -0
- package/dist/types/reveniumConfig.js +3 -0
- package/dist/types/reveniumConfig.js.map +1 -0
- package/dist/types/streamTracker.d.ts +9 -0
- package/dist/types/streamTracker.d.ts.map +1 -0
- package/dist/types/streamTracker.js +3 -0
- package/dist/types/streamTracker.js.map +1 -0
- package/dist/types/subscriber.d.ts +9 -0
- package/dist/types/subscriber.d.ts.map +1 -0
- package/dist/types/subscriber.js +3 -0
- package/dist/types/subscriber.js.map +1 -0
- package/dist/types/tokenCounts.d.ts +6 -0
- package/dist/types/tokenCounts.d.ts.map +1 -0
- package/dist/types/tokenCounts.js +3 -0
- package/dist/types/tokenCounts.js.map +1 -0
- package/dist/types/usageData.d.ts +16 -0
- package/dist/types/usageData.d.ts.map +1 -0
- package/dist/types/usageData.js +3 -0
- package/dist/types/usageData.js.map +1 -0
- package/dist/types/usageMetadata.d.ts +14 -0
- package/dist/types/usageMetadata.d.ts.map +1 -0
- package/dist/types/usageMetadata.js +3 -0
- package/dist/types/usageMetadata.js.map +1 -0
- package/dist/utils/activeMiddleware.d.ts +2 -0
- package/dist/utils/activeMiddleware.d.ts.map +1 -0
- package/dist/utils/activeMiddleware.js +24 -0
- package/dist/utils/activeMiddleware.js.map +1 -0
- package/dist/utils/askConsole.d.ts +2 -0
- package/dist/utils/askConsole.d.ts.map +1 -0
- package/dist/utils/askConsole.js +20 -0
- package/dist/utils/askConsole.js.map +1 -0
- package/dist/utils/calculateDurationMs.d.ts +2 -0
- package/dist/utils/calculateDurationMs.d.ts.map +1 -0
- package/dist/utils/calculateDurationMs.js +7 -0
- package/dist/utils/calculateDurationMs.js.map +1 -0
- package/dist/utils/constants/constants.d.ts +9 -0
- package/dist/utils/constants/constants.d.ts.map +1 -0
- package/dist/utils/constants/constants.js +19 -0
- package/dist/utils/constants/constants.js.map +1 -0
- package/dist/utils/constants/messages.d.ts +19 -0
- package/dist/utils/constants/messages.d.ts.map +1 -0
- package/dist/utils/constants/messages.js +22 -0
- package/dist/utils/constants/messages.js.map +1 -0
- package/dist/utils/createMeteringRequest.d.ts +3 -0
- package/dist/utils/createMeteringRequest.d.ts.map +1 -0
- package/dist/utils/createMeteringRequest.js +10 -0
- package/dist/utils/createMeteringRequest.js.map +1 -0
- package/dist/utils/createPerplexityMetadata.d.ts +3 -0
- package/dist/utils/createPerplexityMetadata.d.ts.map +1 -0
- package/dist/utils/createPerplexityMetadata.js +12 -0
- package/dist/utils/createPerplexityMetadata.js.map +1 -0
- package/dist/utils/extractModelName.d.ts +2 -0
- package/dist/utils/extractModelName.d.ts.map +1 -0
- package/dist/utils/extractModelName.js +15 -0
- package/dist/utils/extractModelName.js.map +1 -0
- package/dist/utils/extractPerplexityTokenCounts.d.ts +3 -0
- package/dist/utils/extractPerplexityTokenCounts.d.ts.map +1 -0
- package/dist/utils/extractPerplexityTokenCounts.js +17 -0
- package/dist/utils/extractPerplexityTokenCounts.js.map +1 -0
- package/dist/utils/extractStopReason.d.ts +2 -0
- package/dist/utils/extractStopReason.d.ts.map +1 -0
- package/dist/utils/extractStopReason.js +14 -0
- package/dist/utils/extractStopReason.js.map +1 -0
- package/dist/utils/extractUsageMetadata.d.ts +3 -0
- package/dist/utils/extractUsageMetadata.d.ts.map +1 -0
- package/dist/utils/extractUsageMetadata.js +15 -0
- package/dist/utils/extractUsageMetadata.js.map +1 -0
- package/dist/utils/formatTimestamp.d.ts +2 -0
- package/dist/utils/formatTimestamp.d.ts.map +1 -0
- package/dist/utils/formatTimestamp.js +7 -0
- package/dist/utils/formatTimestamp.js.map +1 -0
- package/dist/utils/generateTransactionId.d.ts +2 -0
- package/dist/utils/generateTransactionId.d.ts.map +1 -0
- package/dist/utils/generateTransactionId.js +8 -0
- package/dist/utils/generateTransactionId.js.map +1 -0
- package/dist/utils/getEnv.d.ts +4 -0
- package/dist/utils/getEnv.d.ts.map +1 -0
- package/dist/utils/getEnv.js +16 -0
- package/dist/utils/getEnv.js.map +1 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +29 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/safeExtract.d.ts +2 -0
- package/dist/utils/safeExtract.d.ts.map +1 -0
- package/dist/utils/safeExtract.js +22 -0
- package/dist/utils/safeExtract.js.map +1 -0
- package/dist/utils/sendMeteringData.d.ts +3 -0
- package/dist/utils/sendMeteringData.d.ts.map +1 -0
- package/dist/utils/sendMeteringData.js +35 -0
- package/dist/utils/sendMeteringData.js.map +1 -0
- package/dist/utils/verifyLogVerbose.d.ts +2 -0
- package/dist/utils/verifyLogVerbose.d.ts.map +1 -0
- package/dist/utils/verifyLogVerbose.js +10 -0
- package/dist/utils/verifyLogVerbose.js.map +1 -0
- package/examples/README.md +316 -0
- package/examples/basic-client-example.ts +36 -0
- package/examples/metadata-example.ts +92 -0
- package/examples/multiple-models-example.ts +65 -0
- package/examples/openai-client-example.ts +76 -0
- package/examples/perplexity_basic_example.ts +24 -0
- package/examples/perplexity_middleware_example.ts +222 -0
- package/examples/run-all-examples.ts +118 -0
- package/examples/simple-test.ts +278 -0
- package/examples/streaming-example.ts +46 -0
- package/package.json +54 -0
- package/src/index.ts +38 -0
- package/src/models/Exception/ApiResponseError.ts +6 -0
- package/src/models/Exception/ConfigurationError.ts +6 -0
- package/src/models/Exception/MeteringError.ts +6 -0
- package/src/models/Exception/MiddlewareActivationError.ts +6 -0
- package/src/models/Exception/StreamTrackingError.ts +6 -0
- package/src/models/Exception/TokenCountingError.ts +6 -0
- package/src/models/Exception/index.ts +6 -0
- package/src/models/Logger.ts +38 -0
- package/src/models/index.ts +2 -0
- package/src/perplexity-ai/client.ts +97 -0
- package/src/perplexity-ai/index.ts +2 -0
- package/src/perplexity-ai/middleware.ts +259 -0
- package/src/types/context.ts +9 -0
- package/src/types/index.ts +11 -0
- package/src/types/metering.ts +28 -0
- package/src/types/modelInfo.ts +5 -0
- package/src/types/operation.ts +4 -0
- package/src/types/perplexityConfig.ts +4 -0
- package/src/types/provider.ts +3 -0
- package/src/types/reveniumConfig.ts +5 -0
- package/src/types/streamTracker.ts +9 -0
- package/src/types/subscriber.ts +8 -0
- package/src/types/tokenCounts.ts +5 -0
- package/src/types/usageData.ts +16 -0
- package/src/types/usageMetadata.ts +13 -0
- package/src/utils/activeMiddleware.ts +34 -0
- package/src/utils/askConsole.ts +15 -0
- package/src/utils/calculateDurationMs.ts +3 -0
- package/src/utils/constants/constants.ts +17 -0
- package/src/utils/constants/messages.ts +35 -0
- package/src/utils/createMeteringRequest.ts +16 -0
- package/src/utils/createPerplexityMetadata.ts +11 -0
- package/src/utils/extractModelName.ts +14 -0
- package/src/utils/extractPerplexityTokenCounts.ts +16 -0
- package/src/utils/extractStopReason.ts +13 -0
- package/src/utils/extractUsageMetadata.ts +15 -0
- package/src/utils/formatTimestamp.ts +3 -0
- package/src/utils/generateTransactionId.ts +5 -0
- package/src/utils/getEnv.ts +16 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/safeExtract.ts +18 -0
- package/src/utils/sendMeteringData.ts +46 -0
- package/src/utils/verifyLogVerbose.ts +7 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { logger, Logger } from "../models";
|
|
2
|
+
import {
|
|
3
|
+
OperationType,
|
|
4
|
+
Provider,
|
|
5
|
+
UsageMetadata,
|
|
6
|
+
StreamTracker,
|
|
7
|
+
} from "../types";
|
|
8
|
+
import {
|
|
9
|
+
formatTimestamp,
|
|
10
|
+
calculateDurationMs,
|
|
11
|
+
extractPerplexityTokenCounts,
|
|
12
|
+
extractStopReason,
|
|
13
|
+
extractModelName,
|
|
14
|
+
extractUsageMetadata,
|
|
15
|
+
createMeteringRequest,
|
|
16
|
+
sendMeteringData,
|
|
17
|
+
generateTransactionId,
|
|
18
|
+
} from "../utils";
|
|
19
|
+
import { ConfigurationError, StreamTrackingError } from "../models";
|
|
20
|
+
import {
|
|
21
|
+
DEBUG_MIDDLEWARE_ALREADY_INITIALIZED_MESSAGE,
|
|
22
|
+
DEBUG_MIDDLEWARE_FAILED_TO_INITIALIZE_MESSAGE,
|
|
23
|
+
DEBUG_MIDDLEWARE_INITIALIZED_MESSAGE,
|
|
24
|
+
FAILED_TO_INITIALIZE_PERPLEXITY_MIDDLEWARE_MESSAGE,
|
|
25
|
+
DEBUG_MIDDLEWARE_INTERCEPTING_PERPLEXITY_REQUESTS_MESSAGE,
|
|
26
|
+
WARNING_REVENIUM_METERING_API_KEY_NOT_FOUND_MESSAGE,
|
|
27
|
+
FAILED_TO_PARSE_REQUEST_BODY_FOR_METADATA_EXTRACTION_MESSAGE,
|
|
28
|
+
DEBUG_MIDDLEWARE_PERPLEXITY_API_REQUEST_INTERCEPTED_MESSAGE,
|
|
29
|
+
ERROR_PERPLEXITY_API_REQUEST_FAILED_MESSAGE,
|
|
30
|
+
WARNING_FAILED_TO_SEND_METERING_DATA_MESSAGE,
|
|
31
|
+
DEBUG_MIDDLEWARE_PERPLEXITY_API_RESPONSE_METERING_COMPLETED_MESSAGE,
|
|
32
|
+
DEBUG_SKIPPING_METERING_DATA_SEND_MESSAGE,
|
|
33
|
+
ERROR_FAILED_TO_PROCESS_PERPLEXITY_RESPONSE_MESSAGE,
|
|
34
|
+
} from "../utils/constants/messages";
|
|
35
|
+
import {
|
|
36
|
+
CURRENT_PERPLEXITY_BASE_URL,
|
|
37
|
+
DEFAULT_PERPLEXITY_MODEL,
|
|
38
|
+
REVENIUM_METERING_BASE_URL,
|
|
39
|
+
} from "../utils/constants/constants";
|
|
40
|
+
import { Subscriber } from "../types/subscriber";
|
|
41
|
+
import {
|
|
42
|
+
getPerplexityBaseUrl,
|
|
43
|
+
getReveniumApiKey,
|
|
44
|
+
getReveniumBaseUrl,
|
|
45
|
+
} from "../utils/getEnv";
|
|
46
|
+
|
|
47
|
+
// Stream tracking storage
|
|
48
|
+
const streamTrackers: Map<string, StreamTracker> = new Map<
|
|
49
|
+
string,
|
|
50
|
+
StreamTracker
|
|
51
|
+
>();
|
|
52
|
+
|
|
53
|
+
// Track if middleware has been initialized to avoid double initialization
|
|
54
|
+
let middlewareInitialized: boolean = false;
|
|
55
|
+
|
|
56
|
+
//Initialize Perplexity AI middleware.
|
|
57
|
+
function initializePerplexityMiddleware(): void {
|
|
58
|
+
if (middlewareInitialized) {
|
|
59
|
+
console.log(DEBUG_MIDDLEWARE_ALREADY_INITIALIZED_MESSAGE);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Always initialize middleware, even without API key
|
|
64
|
+
if (!getReveniumApiKey()) {
|
|
65
|
+
Logger.warning(WARNING_REVENIUM_METERING_API_KEY_NOT_FOUND_MESSAGE);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Intercept HTTP requests to Perplexity API
|
|
70
|
+
interceptPerplexityHTTPRequests();
|
|
71
|
+
|
|
72
|
+
// Mark as initialized
|
|
73
|
+
middlewareInitialized = true;
|
|
74
|
+
console.log(DEBUG_MIDDLEWARE_INITIALIZED_MESSAGE);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(DEBUG_MIDDLEWARE_FAILED_TO_INITIALIZE_MESSAGE, error);
|
|
77
|
+
throw new ConfigurationError(
|
|
78
|
+
`${FAILED_TO_INITIALIZE_PERPLEXITY_MIDDLEWARE_MESSAGE}: ${error}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//Intercept HTTP requests to Perplexity API
|
|
84
|
+
function interceptPerplexityHTTPRequests(): void {
|
|
85
|
+
// Store original fetch function
|
|
86
|
+
const originalFetch = global.fetch;
|
|
87
|
+
|
|
88
|
+
// Override fetch to intercept Perplexity API calls
|
|
89
|
+
global.fetch = async function (
|
|
90
|
+
input: string | URL | Request,
|
|
91
|
+
init?: RequestInit
|
|
92
|
+
): Promise<Response> {
|
|
93
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
94
|
+
console.log("🔍 [DEBUG] URL:", url);
|
|
95
|
+
|
|
96
|
+
// Check if this is a Perplexity API request
|
|
97
|
+
if (
|
|
98
|
+
url.includes(getPerplexityBaseUrl()) ||
|
|
99
|
+
url.includes("api.perplexity.ai")
|
|
100
|
+
) {
|
|
101
|
+
return await handlePerplexityRequest(url, init, originalFetch);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// For non-Perplexity requests, use original fetch
|
|
105
|
+
return originalFetch(input, init);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
console.log(DEBUG_MIDDLEWARE_INTERCEPTING_PERPLEXITY_REQUESTS_MESSAGE);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//Handle Perplexity API requests
|
|
112
|
+
async function handlePerplexityRequest(
|
|
113
|
+
url: string,
|
|
114
|
+
init: RequestInit | undefined,
|
|
115
|
+
originalFetch: typeof fetch
|
|
116
|
+
): Promise<Response> {
|
|
117
|
+
const startTime = new Date();
|
|
118
|
+
const transactionId = generateTransactionId();
|
|
119
|
+
|
|
120
|
+
// Extract request body for metadata
|
|
121
|
+
let requestBody: any = {};
|
|
122
|
+
if (init?.body) {
|
|
123
|
+
try {
|
|
124
|
+
requestBody =
|
|
125
|
+
typeof init.body === "string" ? JSON.parse(init.body) : init.body;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
Logger.warning(
|
|
128
|
+
FAILED_TO_PARSE_REQUEST_BODY_FOR_METADATA_EXTRACTION_MESSAGE
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Extract usage metadata
|
|
134
|
+
const usageMetadata = extractUsageMetadata(requestBody, null);
|
|
135
|
+
const model = requestBody.model || DEFAULT_PERPLEXITY_MODEL;
|
|
136
|
+
|
|
137
|
+
Logger.debug(DEBUG_MIDDLEWARE_PERPLEXITY_API_REQUEST_INTERCEPTED_MESSAGE, {
|
|
138
|
+
transactionId,
|
|
139
|
+
model,
|
|
140
|
+
url,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Make the original request
|
|
145
|
+
const response = await originalFetch(url, init);
|
|
146
|
+
|
|
147
|
+
// Clone the response so we can read it and still return it
|
|
148
|
+
const responseClone = response.clone();
|
|
149
|
+
|
|
150
|
+
// Process the response asynchronously (don't wait for it)
|
|
151
|
+
processPerplexityResponse(
|
|
152
|
+
responseClone,
|
|
153
|
+
transactionId,
|
|
154
|
+
model,
|
|
155
|
+
startTime,
|
|
156
|
+
usageMetadata
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// Return the original response immediately
|
|
160
|
+
return response;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
Logger.error(ERROR_PERPLEXITY_API_REQUEST_FAILED_MESSAGE, {
|
|
163
|
+
transactionId,
|
|
164
|
+
error,
|
|
165
|
+
});
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//Process Perplexity API response
|
|
171
|
+
async function processPerplexityResponse(
|
|
172
|
+
response: Response,
|
|
173
|
+
transactionId: string,
|
|
174
|
+
model: string,
|
|
175
|
+
startTime: Date,
|
|
176
|
+
usageMetadata?: UsageMetadata,
|
|
177
|
+
subscriber?: Subscriber,
|
|
178
|
+
taskType?: string,
|
|
179
|
+
costType?: string,
|
|
180
|
+
operationType?: string,
|
|
181
|
+
organizationId?: string,
|
|
182
|
+
productId?: string,
|
|
183
|
+
isStreamed?: boolean
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
try {
|
|
186
|
+
const endTime = new Date();
|
|
187
|
+
const duration = calculateDurationMs(startTime, endTime);
|
|
188
|
+
|
|
189
|
+
// Parse response body
|
|
190
|
+
const responseData = await response.json();
|
|
191
|
+
|
|
192
|
+
// Extract token counts and other data
|
|
193
|
+
const tokenCounts = extractPerplexityTokenCounts(responseData);
|
|
194
|
+
const stopReason = extractStopReason(responseData);
|
|
195
|
+
const modelName = extractModelName(responseData, model);
|
|
196
|
+
|
|
197
|
+
// Send metering data (only if API key is available)
|
|
198
|
+
if (getReveniumApiKey()) {
|
|
199
|
+
try {
|
|
200
|
+
await sendMeteringData(
|
|
201
|
+
{
|
|
202
|
+
stopReason: "END",
|
|
203
|
+
costType: costType || "AI",
|
|
204
|
+
isStreamed: isStreamed || true,
|
|
205
|
+
taskType: taskType || "chat",
|
|
206
|
+
agent: "perplexity-ai",
|
|
207
|
+
operationType: operationType || "CHAT",
|
|
208
|
+
inputTokenCount: tokenCounts.inputTokens,
|
|
209
|
+
outputTokenCount: tokenCounts.outputTokens,
|
|
210
|
+
reasoningTokenCount: 0,
|
|
211
|
+
cacheCreationTokenCount: 0,
|
|
212
|
+
cacheReadTokenCount: 0,
|
|
213
|
+
totalTokenCount: tokenCounts.totalTokens,
|
|
214
|
+
organizationId: organizationId || "my-customers-name",
|
|
215
|
+
productId: productId || "free-trial",
|
|
216
|
+
subscriber: subscriber || {
|
|
217
|
+
id: `user-${generateTransactionId()}`,
|
|
218
|
+
email: `user@perplexity.ai`,
|
|
219
|
+
credential: {
|
|
220
|
+
name: "keyname",
|
|
221
|
+
value: "keyValue",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
model: modelName,
|
|
225
|
+
transactionId,
|
|
226
|
+
responseTime: formatTimestamp(endTime),
|
|
227
|
+
requestDuration: duration,
|
|
228
|
+
provider: Provider.PERPLEXITY,
|
|
229
|
+
requestTime: formatTimestamp(startTime),
|
|
230
|
+
completionStartTime: formatTimestamp(endTime),
|
|
231
|
+
timeToFirstToken: 0,
|
|
232
|
+
middleware_source: "node",
|
|
233
|
+
},
|
|
234
|
+
getReveniumApiKey(),
|
|
235
|
+
getReveniumBaseUrl()
|
|
236
|
+
);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
Logger.warning(WARNING_FAILED_TO_SEND_METERING_DATA_MESSAGE, { error });
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
Logger.debug(DEBUG_SKIPPING_METERING_DATA_SEND_MESSAGE);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
Logger.debug(
|
|
245
|
+
DEBUG_MIDDLEWARE_PERPLEXITY_API_RESPONSE_METERING_COMPLETED_MESSAGE,
|
|
246
|
+
{
|
|
247
|
+
transactionId,
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
Logger.error(ERROR_FAILED_TO_PROCESS_PERPLEXITY_RESPONSE_MESSAGE, {
|
|
252
|
+
transactionId,
|
|
253
|
+
error,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Initialize middleware when this module is imported
|
|
259
|
+
initializePerplexityMiddleware();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./context";
|
|
2
|
+
export * from "./perplexityConfig";
|
|
3
|
+
export * from "./metering";
|
|
4
|
+
export * from "./modelInfo";
|
|
5
|
+
export * from "./operation";
|
|
6
|
+
export * from "./provider";
|
|
7
|
+
export * from "./reveniumConfig";
|
|
8
|
+
export * from "./streamTracker";
|
|
9
|
+
export * from "./tokenCounts";
|
|
10
|
+
export * from "./usageData";
|
|
11
|
+
export * from "./usageMetadata";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Subscriber } from "./subscriber";
|
|
2
|
+
|
|
3
|
+
export interface MeteringRequest {
|
|
4
|
+
stopReason: string;
|
|
5
|
+
costType: string;
|
|
6
|
+
isStreamed: boolean;
|
|
7
|
+
taskType: string;
|
|
8
|
+
agent: string;
|
|
9
|
+
operationType: string;
|
|
10
|
+
inputTokenCount: number;
|
|
11
|
+
outputTokenCount: number;
|
|
12
|
+
reasoningTokenCount: number;
|
|
13
|
+
cacheCreationTokenCount: number;
|
|
14
|
+
cacheReadTokenCount: number;
|
|
15
|
+
totalTokenCount: number;
|
|
16
|
+
organizationId: string;
|
|
17
|
+
productId: string;
|
|
18
|
+
subscriber: Subscriber;
|
|
19
|
+
model: string;
|
|
20
|
+
transactionId: string;
|
|
21
|
+
responseTime: string;
|
|
22
|
+
requestDuration: number;
|
|
23
|
+
provider: string;
|
|
24
|
+
requestTime: string;
|
|
25
|
+
completionStartTime: string;
|
|
26
|
+
timeToFirstToken: number;
|
|
27
|
+
middleware_source: string;
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TokenCounts } from "./tokenCounts";
|
|
2
|
+
import { UsageMetadata } from "./usageMetadata";
|
|
3
|
+
|
|
4
|
+
export interface UsageData {
|
|
5
|
+
transactionId: string;
|
|
6
|
+
model: string;
|
|
7
|
+
tokenCounts: TokenCounts;
|
|
8
|
+
stopReason: string;
|
|
9
|
+
startTime: string;
|
|
10
|
+
endTime: string;
|
|
11
|
+
duration: number;
|
|
12
|
+
operationType: string;
|
|
13
|
+
isStreaming: boolean;
|
|
14
|
+
streamId: string;
|
|
15
|
+
usageMetadata?: UsageMetadata;
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface UsageMetadata {
|
|
2
|
+
traceId?: string;
|
|
3
|
+
taskType?: string;
|
|
4
|
+
subscriberEmail?: string;
|
|
5
|
+
subscriberId?: string;
|
|
6
|
+
subscriberCredentialName?: string;
|
|
7
|
+
subscriberCredential?: string;
|
|
8
|
+
organizationId?: string;
|
|
9
|
+
subscriptionId?: string;
|
|
10
|
+
productId?: string;
|
|
11
|
+
agent?: string;
|
|
12
|
+
responseQualityScore?: number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Logger } from "../models";
|
|
2
|
+
import { MiddlewareActivationError } from "../models";
|
|
3
|
+
import { verifyLogVerbose } from "./verifyLogVerbose";
|
|
4
|
+
|
|
5
|
+
export function activeMiddleware(
|
|
6
|
+
verboseStartup: boolean,
|
|
7
|
+
activeSDKs: string[],
|
|
8
|
+
sdkType: string
|
|
9
|
+
): void {
|
|
10
|
+
try {
|
|
11
|
+
verifyLogVerbose(
|
|
12
|
+
verboseStartup,
|
|
13
|
+
`Attempting to activate ${sdkType} middleware...`
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
// For Perplexity, we'll intercept HTTP requests to the Perplexity API
|
|
17
|
+
// This is a simpler approach since Perplexity doesn't have an official SDK
|
|
18
|
+
interceptPerplexityRequests();
|
|
19
|
+
|
|
20
|
+
activeSDKs.push(sdkType);
|
|
21
|
+
verifyLogVerbose(
|
|
22
|
+
verboseStartup,
|
|
23
|
+
`${sdkType} middleware activated successfully`
|
|
24
|
+
);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
Logger.warning(`Failed to activate ${sdkType} middleware: ${error}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function interceptPerplexityRequests(): void {
|
|
31
|
+
// This will be implemented in the main middleware file
|
|
32
|
+
// For now, we just log that we're ready to intercept
|
|
33
|
+
Logger.debug("Perplexity request interceptor ready");
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import readline from "readline";
|
|
2
|
+
|
|
3
|
+
export function askConsole(question: string): Promise<string> {
|
|
4
|
+
const rl = readline.createInterface({
|
|
5
|
+
input: process.stdin,
|
|
6
|
+
output: process.stdout,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
rl.question(question, (content) => {
|
|
11
|
+
rl.close();
|
|
12
|
+
resolve(content);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const TIMEOUT_MS = 10000; // 10 seconds
|
|
2
|
+
export const STATUS_CODE: Record<string, number> = {
|
|
3
|
+
SUCCESS: 200,
|
|
4
|
+
CREATED: 201,
|
|
5
|
+
};
|
|
6
|
+
export const SAFE_EXTRACT: Record<string, string> = {
|
|
7
|
+
INPUT_TOKENS: "usage.prompt_tokens",
|
|
8
|
+
OUTPUT_TOKENS: "usage.completion_tokens",
|
|
9
|
+
TOTAL_TOKENS: "usage.total_tokens",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const MODEL: string = "model";
|
|
13
|
+
export const REVENIUM_METERING_BASE_URL: string =
|
|
14
|
+
"https://api.revenium.io/meter/v2";
|
|
15
|
+
export const CURRENT_PERPLEXITY_BASE_URL: string = "https://api.perplexity.ai";
|
|
16
|
+
export const DEFAULT_PERPLEXITY_MODEL: string = "perplexity-online";
|
|
17
|
+
export const LOG_LEVELS: string[] = ["DEBUG", "INFO", "WARNING", "ERROR"];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const PERPLEXITY_REQUIRED_API_KEY_MESSAGE =
|
|
2
|
+
"PERPLEXITY_API_KEY is required. Set it in environment variables or pass it to the constructor.";
|
|
3
|
+
export const PERPLEXITY_CLIENT_INITIALIZED_MESSAGE =
|
|
4
|
+
"Perplexity AI client initialized with middleware";
|
|
5
|
+
export const PERPLEXITY_CHAT_COMPLETION_FAILED_MESSAGE =
|
|
6
|
+
"Chat completion failed";
|
|
7
|
+
export const PERPLEXITY_STREAMING_CHAT_COMPLETION_FAILED_MESSAGE =
|
|
8
|
+
"Streaming chat completion failed";
|
|
9
|
+
export const PERPLEXITY_GET_MODELS_FAILED_MESSAGE = "Failed to get models";
|
|
10
|
+
export const DEBUG_MIDDLEWARE_ALREADY_INITIALIZED_MESSAGE =
|
|
11
|
+
"[DEBUG] Middleware already initialized, skipping...";
|
|
12
|
+
export const WARNING_REVENIUM_METERING_API_KEY_NOT_FOUND_MESSAGE =
|
|
13
|
+
"REVENIUM_METERING_API_KEY not found - metering will be disabled but middleware will still intercept calls";
|
|
14
|
+
export const DEBUG_MIDDLEWARE_INITIALIZED_MESSAGE =
|
|
15
|
+
"🔍 [DEBUG] Perplexity middleware initialized successfully";
|
|
16
|
+
export const DEBUG_MIDDLEWARE_FAILED_TO_INITIALIZE_MESSAGE =
|
|
17
|
+
"🔍 [DEBUG] Failed to initialize Perplexity middleware";
|
|
18
|
+
export const FAILED_TO_INITIALIZE_PERPLEXITY_MIDDLEWARE_MESSAGE =
|
|
19
|
+
"Failed to initialize Perplexity middleware";
|
|
20
|
+
export const DEBUG_MIDDLEWARE_INTERCEPTING_PERPLEXITY_REQUESTS_MESSAGE =
|
|
21
|
+
"🔍 [DEBUG] Perplexity HTTP interceptor installed";
|
|
22
|
+
export const FAILED_TO_PARSE_REQUEST_BODY_FOR_METADATA_EXTRACTION_MESSAGE =
|
|
23
|
+
"Failed to parse request body for metadata extraction";
|
|
24
|
+
export const DEBUG_MIDDLEWARE_PERPLEXITY_API_REQUEST_INTERCEPTED_MESSAGE =
|
|
25
|
+
"🔍 [DEBUG] Perplexity API request intercepted";
|
|
26
|
+
export const ERROR_PERPLEXITY_API_REQUEST_FAILED_MESSAGE =
|
|
27
|
+
"Perplexity API request failed";
|
|
28
|
+
export const WARNING_FAILED_TO_SEND_METERING_DATA_MESSAGE =
|
|
29
|
+
"Failed to send metering data, but continuing with response";
|
|
30
|
+
export const DEBUG_MIDDLEWARE_PERPLEXITY_API_RESPONSE_METERING_COMPLETED_MESSAGE =
|
|
31
|
+
"🔍 [DEBUG] Perplexity API response metering completed";
|
|
32
|
+
export const DEBUG_SKIPPING_METERING_DATA_SEND_MESSAGE =
|
|
33
|
+
"Skipping metering data send - no API key available";
|
|
34
|
+
export const ERROR_FAILED_TO_PROCESS_PERPLEXITY_RESPONSE_MESSAGE =
|
|
35
|
+
"Failed to process Perplexity response";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MeteringRequest,
|
|
3
|
+
OperationType,
|
|
4
|
+
Provider,
|
|
5
|
+
UsageMetadata,
|
|
6
|
+
} from "../types";
|
|
7
|
+
import { formatTimestamp } from "./formatTimestamp";
|
|
8
|
+
|
|
9
|
+
export function createMeteringRequest(
|
|
10
|
+
request: MeteringRequest
|
|
11
|
+
): MeteringRequest {
|
|
12
|
+
return {
|
|
13
|
+
...request,
|
|
14
|
+
middleware_source: "node",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MODEL } from "./constants/constants";
|
|
2
|
+
import { safeExtract } from "./safeExtract";
|
|
3
|
+
|
|
4
|
+
export function extractModelName(response: any, defaultModel: string): string {
|
|
5
|
+
// Try to extract model name from response
|
|
6
|
+
const modelFromResponse = safeExtract(response, MODEL, "");
|
|
7
|
+
|
|
8
|
+
if (modelFromResponse) {
|
|
9
|
+
return modelFromResponse;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Fallback to default model
|
|
13
|
+
return defaultModel;
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TokenCounts } from "../types";
|
|
2
|
+
import { SAFE_EXTRACT } from "./constants/constants";
|
|
3
|
+
import { safeExtract } from "./safeExtract";
|
|
4
|
+
|
|
5
|
+
export function extractPerplexityTokenCounts(response: any): TokenCounts {
|
|
6
|
+
// Extract token counts from Perplexity response
|
|
7
|
+
const inputTokens = safeExtract(response, SAFE_EXTRACT.INPUT_TOKENS, 0);
|
|
8
|
+
const outputTokens = safeExtract(response, SAFE_EXTRACT.OUTPUT_TOKENS, 0);
|
|
9
|
+
const totalTokens = safeExtract(response, SAFE_EXTRACT.TOTAL_TOKENS, 0);
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
inputTokens,
|
|
13
|
+
outputTokens,
|
|
14
|
+
totalTokens: totalTokens || inputTokens + outputTokens,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { safeExtract } from "./safeExtract";
|
|
2
|
+
|
|
3
|
+
export function extractStopReason(response: any): string {
|
|
4
|
+
// Try to extract stop reason from Perplexity response
|
|
5
|
+
const stopReason = safeExtract(response, "choices.0.finish_reason", "");
|
|
6
|
+
|
|
7
|
+
if (stopReason) {
|
|
8
|
+
return stopReason;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Fallback to a default reason
|
|
12
|
+
return "completed";
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UsageMetadata } from '../types';
|
|
2
|
+
|
|
3
|
+
export function extractUsageMetadata(request: any, context: any): UsageMetadata | undefined {
|
|
4
|
+
// Check if usageMetadata is directly in the request
|
|
5
|
+
if (request && request.usageMetadata) {
|
|
6
|
+
return request.usageMetadata;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Check if usageMetadata is in the context (for model-level metadata)
|
|
10
|
+
if (context && context._reveniumUsageMetadata) {
|
|
11
|
+
return context._reveniumUsageMetadata;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CURRENT_PERPLEXITY_BASE_URL,
|
|
3
|
+
REVENIUM_METERING_BASE_URL,
|
|
4
|
+
} from "./constants/constants";
|
|
5
|
+
|
|
6
|
+
export function getReveniumApiKey(): string {
|
|
7
|
+
return process.env.REVENIUM_METERING_API_KEY ?? "";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getReveniumBaseUrl(): string {
|
|
11
|
+
return process.env.REVENIUM_METERING_BASE_URL || REVENIUM_METERING_BASE_URL;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getPerplexityBaseUrl(): string {
|
|
15
|
+
return process.env.PERPLEXITY_BASE_URL || CURRENT_PERPLEXITY_BASE_URL;
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./activeMiddleware";
|
|
2
|
+
export * from "./calculateDurationMs";
|
|
3
|
+
export * from "./createMeteringRequest";
|
|
4
|
+
export * from "./createPerplexityMetadata";
|
|
5
|
+
export * from "./extractModelName";
|
|
6
|
+
export * from "./extractPerplexityTokenCounts";
|
|
7
|
+
export * from "./extractStopReason";
|
|
8
|
+
export * from "./extractUsageMetadata";
|
|
9
|
+
export * from "./formatTimestamp";
|
|
10
|
+
export * from "./generateTransactionId";
|
|
11
|
+
export * from "./safeExtract";
|
|
12
|
+
export * from "./sendMeteringData";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function safeExtract<T>(obj: any, path: string, defaultValue: T): T {
|
|
2
|
+
try {
|
|
3
|
+
const keys = path.split(".");
|
|
4
|
+
let result = obj;
|
|
5
|
+
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (result && typeof result === "object" && key in result) {
|
|
8
|
+
result = result[key];
|
|
9
|
+
} else {
|
|
10
|
+
return defaultValue;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return result ?? defaultValue;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return defaultValue;
|
|
17
|
+
}
|
|
18
|
+
}
|