@revenium/perplexity 1.0.25 → 2.0.1
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/README.md +443 -568
- package/dist/cjs/core/config/perplexity-config.js +45 -0
- package/dist/cjs/core/config/perplexity-config.js.map +1 -0
- package/dist/cjs/core/config/revenium-config.js +80 -0
- package/dist/cjs/core/config/revenium-config.js.map +1 -0
- package/dist/cjs/core/tracking/metering.js +131 -0
- package/dist/cjs/core/tracking/metering.js.map +1 -0
- package/dist/cjs/core/wrapper/perplexity-client.js +177 -0
- package/dist/cjs/core/wrapper/perplexity-client.js.map +1 -0
- package/dist/cjs/index.js +64 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/types/index.js +21 -0
- package/dist/cjs/types/index.js.map +1 -0
- package/dist/cjs/utils/logger.js +23 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/esm/core/config/perplexity-config.js +40 -0
- package/dist/esm/core/config/perplexity-config.js.map +1 -0
- package/dist/esm/core/config/revenium-config.js +72 -0
- package/dist/esm/core/config/revenium-config.js.map +1 -0
- package/dist/esm/core/tracking/metering.js +126 -0
- package/dist/esm/core/tracking/metering.js.map +1 -0
- package/dist/esm/core/wrapper/perplexity-client.js +170 -0
- package/dist/esm/core/wrapper/perplexity-client.js.map +1 -0
- package/dist/esm/index.js +44 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types/index.js +18 -0
- package/dist/esm/types/index.js.map +1 -0
- package/dist/esm/utils/logger.js +20 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/types/core/config/perplexity-config.d.ts +24 -0
- package/dist/types/core/config/perplexity-config.d.ts.map +1 -0
- package/dist/types/core/config/revenium-config.d.ts +37 -0
- package/dist/types/core/config/revenium-config.d.ts.map +1 -0
- package/dist/types/core/tracking/metering.d.ts +31 -0
- package/dist/types/core/tracking/metering.d.ts.map +1 -0
- package/dist/types/core/wrapper/perplexity-client.d.ts +32 -0
- package/dist/types/core/wrapper/perplexity-client.d.ts.map +1 -0
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/types/index.d.ts +159 -0
- package/dist/types/types/index.d.ts.map +1 -0
- package/dist/types/utils/logger.d.ts +10 -0
- package/dist/types/utils/logger.d.ts.map +1 -0
- package/package.json +36 -31
- package/.env.example +0 -3
- package/dist/interfaces/meteringResponse.d.ts +0 -28
- package/dist/interfaces/meteringResponse.js +0 -2
- package/dist/models/Metering.js +0 -83
- package/dist/v1/perplexityV1.service.js +0 -166
- package/dist/v2/perplexityV2.service.js +0 -178
- package/examples/v1/basic.ts +0 -50
- package/examples/v1/chat.ts +0 -40
- package/examples/v1/metadata.ts +0 -49
- package/examples/v1/streaming.ts +0 -44
- package/examples/v2/basic.ts +0 -49
- package/examples/v2/chat.ts +0 -60
- package/examples/v2/metadata.ts +0 -71
- package/examples/v2/streaming.ts +0 -61
- package/playground/v1/basic.js +0 -50
- package/playground/v1/chat.js +0 -46
- package/playground/v1/metadata.js +0 -50
- package/playground/v1/streaming.js +0 -44
- package/playground/v2/basic.js +0 -49
- package/playground/v2/chat.js +0 -72
- package/playground/v2/metadata.js +0 -76
- package/playground/v2/streaming.js +0 -67
- package/src/index.ts +0 -17
- package/src/interfaces/chatCompletionRequest.ts +0 -15
- package/src/interfaces/credential.ts +0 -4
- package/src/interfaces/meteringRequest.ts +0 -14
- package/src/interfaces/meteringResponse.ts +0 -29
- package/src/interfaces/operation.ts +0 -4
- package/src/interfaces/perplexityResponse.ts +0 -63
- package/src/interfaces/perplexityStreaming.ts +0 -56
- package/src/interfaces/subscriber.ts +0 -8
- package/src/interfaces/tokenCounts.ts +0 -7
- package/src/interfaces/usageMetadata.ts +0 -27
- package/src/models/Logger.ts +0 -38
- package/src/models/Metering.ts +0 -121
- package/src/utils/calculateDurationMs.ts +0 -3
- package/src/utils/constants/constants.ts +0 -10
- package/src/utils/constants/logLevels.ts +0 -1
- package/src/utils/constants/messages.ts +0 -11
- package/src/utils/constants/models.ts +0 -20
- package/src/utils/constants/perplexityModels.ts +0 -71
- package/src/utils/extractTokenCount.ts +0 -26
- package/src/utils/formatTimestamp.ts +0 -3
- package/src/utils/generateTransactionId.ts +0 -5
- package/src/utils/index.ts +0 -39
- package/src/utils/loadEnv.ts +0 -8
- package/src/utils/safeExtract.ts +0 -67
- package/src/v1/perplexityV1.controller.ts +0 -164
- package/src/v1/perplexityV1.service.ts +0 -230
- package/src/v2/perplexityV2.controller.ts +0 -219
- package/src/v2/perplexityV2.service.ts +0 -260
- package/tsconfig.json +0 -15
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import OpenAI from "openai";
|
|
2
|
-
import { logger } from "../models/Logger";
|
|
3
|
-
import { Metering } from "../models/Metering";
|
|
4
|
-
import { generateTransactionId } from "../utils/generateTransactionId";
|
|
5
|
-
import { IOperationType } from "../interfaces/operation";
|
|
6
|
-
import { IUsageMetadata } from "../interfaces/usageMetadata";
|
|
7
|
-
import { ITokenCounts } from "../interfaces/tokenCounts";
|
|
8
|
-
import {
|
|
9
|
-
IChatCompletionRequest,
|
|
10
|
-
IPerplexityMessage,
|
|
11
|
-
} from "../interfaces/chatCompletionRequest";
|
|
12
|
-
import {
|
|
13
|
-
IPerplexityResponseChat,
|
|
14
|
-
IPerplexityRawResponse,
|
|
15
|
-
} from "../interfaces/perplexityResponse";
|
|
16
|
-
import {
|
|
17
|
-
IPerplexityStreamingResponse,
|
|
18
|
-
IPerplexityStreamChunk,
|
|
19
|
-
} from "../interfaces/perplexityStreaming";
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
PERPLEXITY_API_BASE_URL,
|
|
23
|
-
PERPLEXITY_API_KEY,
|
|
24
|
-
PERPLEXITY_CLIENT_INITIALIZED_MESSAGE,
|
|
25
|
-
REVENIUM_METERING_API_KEY,
|
|
26
|
-
REVENIUM_METERING_BASE_URL,
|
|
27
|
-
} from "../utils";
|
|
28
|
-
|
|
29
|
-
export class PerplexityV1Service {
|
|
30
|
-
private client: OpenAI;
|
|
31
|
-
private working: boolean = true;
|
|
32
|
-
|
|
33
|
-
constructor() {
|
|
34
|
-
this.client = new OpenAI({
|
|
35
|
-
apiKey: PERPLEXITY_API_KEY,
|
|
36
|
-
baseURL: PERPLEXITY_API_BASE_URL,
|
|
37
|
-
});
|
|
38
|
-
logger.info(PERPLEXITY_CLIENT_INITIALIZED_MESSAGE);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async createChatCompletion(
|
|
42
|
-
params: IChatCompletionRequest,
|
|
43
|
-
model: string
|
|
44
|
-
): Promise<IPerplexityResponseChat> {
|
|
45
|
-
const transactionId = generateTransactionId();
|
|
46
|
-
const startTime = new Date();
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const { usageMetadata, ...openaiParams } = params;
|
|
50
|
-
const requestParams = {
|
|
51
|
-
...openaiParams,
|
|
52
|
-
model,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const result = (await this.client.chat.completions.create({
|
|
56
|
-
...requestParams,
|
|
57
|
-
stream: false,
|
|
58
|
-
})) as IPerplexityRawResponse;
|
|
59
|
-
|
|
60
|
-
// Build V1 response format
|
|
61
|
-
const perplexityResponse: IPerplexityResponseChat = {
|
|
62
|
-
responses: [
|
|
63
|
-
{
|
|
64
|
-
text: result.choices[0]?.message?.content || "",
|
|
65
|
-
role: "assistant",
|
|
66
|
-
finishReason: result.choices[0]?.finish_reason,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
usageMetadata: {
|
|
70
|
-
inputTokenCount: result.usage?.prompt_tokens || 0,
|
|
71
|
-
outputTokenCount: result.usage?.completion_tokens || 0,
|
|
72
|
-
totalTokenCount: result.usage?.total_tokens || 0,
|
|
73
|
-
},
|
|
74
|
-
modelVersion: result.model,
|
|
75
|
-
transactionId,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Send metering data (using current middleware logic)
|
|
79
|
-
if (this.working) {
|
|
80
|
-
const tokenCounts: ITokenCounts = {
|
|
81
|
-
inputTokens: result.usage?.prompt_tokens || 0,
|
|
82
|
-
outputTokens: result.usage?.completion_tokens || 0,
|
|
83
|
-
totalTokens: result.usage?.total_tokens || 0,
|
|
84
|
-
};
|
|
85
|
-
const endTime = new Date();
|
|
86
|
-
logger.info("Metering is working.");
|
|
87
|
-
|
|
88
|
-
const metering = new Metering(
|
|
89
|
-
REVENIUM_METERING_API_KEY ?? "",
|
|
90
|
-
REVENIUM_METERING_BASE_URL ?? ""
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
// Combine user metadata with transaction ID
|
|
94
|
-
const combinedMetadata: IUsageMetadata = {
|
|
95
|
-
...usageMetadata,
|
|
96
|
-
transactionId,
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const getMetering = metering.createMetering(
|
|
100
|
-
{
|
|
101
|
-
modelName: model,
|
|
102
|
-
endTime,
|
|
103
|
-
startTime,
|
|
104
|
-
operationType: IOperationType.CHAT,
|
|
105
|
-
stopReason: "END",
|
|
106
|
-
tokenCounts,
|
|
107
|
-
usageMetadata: combinedMetadata,
|
|
108
|
-
},
|
|
109
|
-
false
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
await metering.sendMeteringData(getMetering);
|
|
113
|
-
} else {
|
|
114
|
-
logger.warning("Metering is not working. Check your configuration.");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return perplexityResponse;
|
|
118
|
-
} catch (error) {
|
|
119
|
-
logger.error("Error in Perplexity V1 chat completion:", error);
|
|
120
|
-
throw error;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async createStreamingCompletion(
|
|
125
|
-
params: IChatCompletionRequest,
|
|
126
|
-
model: string
|
|
127
|
-
): Promise<IPerplexityStreamingResponse> {
|
|
128
|
-
const transactionId = generateTransactionId();
|
|
129
|
-
const startTime = new Date();
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const { usageMetadata, ...openaiParams } = params;
|
|
133
|
-
const requestParams = {
|
|
134
|
-
...openaiParams,
|
|
135
|
-
model,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const stream = await this.client.chat.completions.create({
|
|
139
|
-
...requestParams,
|
|
140
|
-
stream: true,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const wrappedStream = this.wrapStream(
|
|
144
|
-
stream as any,
|
|
145
|
-
transactionId,
|
|
146
|
-
model,
|
|
147
|
-
startTime,
|
|
148
|
-
usageMetadata
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
stream: wrappedStream,
|
|
153
|
-
usageMetadata: {
|
|
154
|
-
inputTokenCount: 0, // Will be updated when stream completes
|
|
155
|
-
outputTokenCount: 0,
|
|
156
|
-
totalTokenCount: 0,
|
|
157
|
-
},
|
|
158
|
-
modelVersion: model,
|
|
159
|
-
transactionId,
|
|
160
|
-
};
|
|
161
|
-
} catch (error) {
|
|
162
|
-
logger.error("Error in Perplexity V1 streaming:", error);
|
|
163
|
-
throw error;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private async *wrapStream(
|
|
168
|
-
stream: AsyncIterable<any>,
|
|
169
|
-
transactionId: string,
|
|
170
|
-
model: string,
|
|
171
|
-
startTime: Date,
|
|
172
|
-
usageMetadata?: IUsageMetadata
|
|
173
|
-
): AsyncGenerator<IPerplexityStreamChunk> {
|
|
174
|
-
let lastChunk: any = null;
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
for await (const chunk of stream) {
|
|
178
|
-
lastChunk = chunk;
|
|
179
|
-
yield chunk as IPerplexityStreamChunk;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Send metering data when stream completes (using current middleware logic)
|
|
183
|
-
if (this.working) {
|
|
184
|
-
// For streaming, we'll use the last chunk's usage data if available
|
|
185
|
-
const tokenCounts: ITokenCounts = {
|
|
186
|
-
inputTokens: 0, // Will be updated if lastChunk has usage data
|
|
187
|
-
outputTokens: 0,
|
|
188
|
-
totalTokens: 0,
|
|
189
|
-
};
|
|
190
|
-
const endTime = new Date();
|
|
191
|
-
logger.info("Metering is working.");
|
|
192
|
-
|
|
193
|
-
const metering = new Metering(
|
|
194
|
-
REVENIUM_METERING_API_KEY ?? "",
|
|
195
|
-
REVENIUM_METERING_BASE_URL ?? ""
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
// Combine user metadata with transaction ID
|
|
199
|
-
const combinedMetadata: IUsageMetadata = {
|
|
200
|
-
...usageMetadata,
|
|
201
|
-
transactionId,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const getMetering = metering.createMetering(
|
|
205
|
-
{
|
|
206
|
-
modelName: model,
|
|
207
|
-
endTime,
|
|
208
|
-
startTime,
|
|
209
|
-
operationType: IOperationType.CHAT,
|
|
210
|
-
stopReason: "END",
|
|
211
|
-
tokenCounts,
|
|
212
|
-
usageMetadata: combinedMetadata,
|
|
213
|
-
},
|
|
214
|
-
true
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
await metering.sendMeteringData(getMetering);
|
|
218
|
-
} else {
|
|
219
|
-
logger.warning("Metering is not working. Check your configuration.");
|
|
220
|
-
}
|
|
221
|
-
} catch (error) {
|
|
222
|
-
logger.error("Error in Perplexity V1 stream processing:", error);
|
|
223
|
-
throw error;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
setWorking(working: boolean) {
|
|
228
|
-
this.working = working;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { logger } from "../models/Logger";
|
|
2
|
-
import { IUsageMetadata } from "../interfaces/usageMetadata";
|
|
3
|
-
import {
|
|
4
|
-
IChatCompletionRequest,
|
|
5
|
-
IPerplexityMessage,
|
|
6
|
-
} from "../interfaces/chatCompletionRequest";
|
|
7
|
-
import { IPerplexityV2Response } from "../interfaces/perplexityResponse";
|
|
8
|
-
import { IPerplexityV2StreamingResponse } from "../interfaces/perplexityStreaming";
|
|
9
|
-
import {
|
|
10
|
-
DEFAULT_CHAT_MODEL,
|
|
11
|
-
PERPLEXITY_MODELS,
|
|
12
|
-
MODEL_CAPABILITIES,
|
|
13
|
-
} from "../utils/constants/perplexityModels";
|
|
14
|
-
import { PerplexityV2Service } from "./perplexityV2.service";
|
|
15
|
-
import { models } from "../utils";
|
|
16
|
-
|
|
17
|
-
export class PerplexityV2Controller {
|
|
18
|
-
private service: PerplexityV2Service;
|
|
19
|
-
|
|
20
|
-
constructor() {
|
|
21
|
-
this.service = new PerplexityV2Service();
|
|
22
|
-
logger.info(
|
|
23
|
-
"Perplexity V2 Controller initialized (Enhanced Response Format)"
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Create a chat completion using Perplexity V2 API (Enhanced Response Format following OpenAI Responses API)
|
|
29
|
-
* @param messages Array of message strings or OpenAI message objects
|
|
30
|
-
* @param model Optional model name (defaults to sonar-pro)
|
|
31
|
-
* @param customMetadata Optional custom metadata for tracking
|
|
32
|
-
* @returns Promise<IPerplexityV2Response>
|
|
33
|
-
*/
|
|
34
|
-
async createChat(
|
|
35
|
-
messages: string[] | Array<{ role: string; content: string }>,
|
|
36
|
-
model: string = DEFAULT_CHAT_MODEL,
|
|
37
|
-
customMetadata?: IUsageMetadata
|
|
38
|
-
): Promise<IPerplexityV2Response> {
|
|
39
|
-
try {
|
|
40
|
-
// Normalize messages to OpenAI format
|
|
41
|
-
const normalizedMessages = this.normalizeMessages(messages);
|
|
42
|
-
|
|
43
|
-
// Validate model
|
|
44
|
-
this.validateModel(model);
|
|
45
|
-
|
|
46
|
-
// Create chat completion request
|
|
47
|
-
const request: IChatCompletionRequest = {
|
|
48
|
-
messages: normalizedMessages,
|
|
49
|
-
usageMetadata: customMetadata,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
logger.info(
|
|
53
|
-
`Creating Perplexity V2 chat completion with model: ${model}`
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
return await this.service.createChatCompletion(request, model);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
logger.error("Error in Perplexity V2 createChat:", error);
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Create a streaming chat completion using Perplexity V2 API (Enhanced Response Format following OpenAI Responses API)
|
|
65
|
-
* @param messages Array of message strings or OpenAI message objects
|
|
66
|
-
* @param model Optional model name (defaults to sonar-pro)
|
|
67
|
-
* @param customMetadata Optional custom metadata for tracking
|
|
68
|
-
* @returns Promise<IPerplexityV2StreamingResponse>
|
|
69
|
-
*/
|
|
70
|
-
async createStreaming(
|
|
71
|
-
messages: string[] | Array<{ role: string; content: string }>,
|
|
72
|
-
model: string = DEFAULT_CHAT_MODEL,
|
|
73
|
-
customMetadata?: IUsageMetadata
|
|
74
|
-
): Promise<IPerplexityV2StreamingResponse> {
|
|
75
|
-
try {
|
|
76
|
-
// Normalize messages to OpenAI format
|
|
77
|
-
const normalizedMessages = this.normalizeMessages(messages);
|
|
78
|
-
|
|
79
|
-
// Validate model
|
|
80
|
-
this.validateModel(model);
|
|
81
|
-
|
|
82
|
-
// Create streaming request
|
|
83
|
-
const request: IChatCompletionRequest = {
|
|
84
|
-
messages: normalizedMessages,
|
|
85
|
-
usageMetadata: customMetadata,
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
logger.info(
|
|
89
|
-
`Creating Perplexity V2 streaming completion with model: ${model}`
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return await this.service.createStreamingCompletion(request, model);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.error("Error in Perplexity V2 createStreaming:", error);
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Get available models for Perplexity V2
|
|
101
|
-
* @returns Array of available model names
|
|
102
|
-
*/
|
|
103
|
-
getAvailableModels(): string[] {
|
|
104
|
-
return models; // Use the existing models from utils
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check if a model supports a specific capability
|
|
109
|
-
* @param model Model name
|
|
110
|
-
* @param capability Capability to check (chat, streaming, online)
|
|
111
|
-
* @returns boolean
|
|
112
|
-
*/
|
|
113
|
-
supportsCapability(
|
|
114
|
-
model: string,
|
|
115
|
-
capability: "chat" | "streaming" | "online"
|
|
116
|
-
): boolean {
|
|
117
|
-
return MODEL_CAPABILITIES[model]?.[capability] || false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Get model information including capabilities and context window (V2 Enhanced)
|
|
122
|
-
* @param model Model name
|
|
123
|
-
* @returns Enhanced model information object
|
|
124
|
-
*/
|
|
125
|
-
getModelInfo(model: string) {
|
|
126
|
-
const capabilities = MODEL_CAPABILITIES[model];
|
|
127
|
-
if (!capabilities) {
|
|
128
|
-
throw new Error(`Model "${model}" is not supported`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
model,
|
|
133
|
-
capabilities: {
|
|
134
|
-
chat: capabilities.chat,
|
|
135
|
-
streaming: capabilities.streaming,
|
|
136
|
-
online: capabilities.online,
|
|
137
|
-
},
|
|
138
|
-
contextWindow: capabilities.contextWindow,
|
|
139
|
-
version: "v2",
|
|
140
|
-
enhanced: true,
|
|
141
|
-
responseFormat: "openai-responses-api",
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get recommended model for a specific use case (V2 Enhanced)
|
|
147
|
-
* @param useCase Use case (chat, streaming, online-search, offline-chat)
|
|
148
|
-
* @returns Recommended model name with reasoning
|
|
149
|
-
*/
|
|
150
|
-
getRecommendedModel(
|
|
151
|
-
useCase: "chat" | "streaming" | "online-search" | "offline-chat"
|
|
152
|
-
) {
|
|
153
|
-
const recommendations = {
|
|
154
|
-
"online-search": {
|
|
155
|
-
model: PERPLEXITY_MODELS.SONAR_PRO,
|
|
156
|
-
reason: "Best for online search with high accuracy",
|
|
157
|
-
},
|
|
158
|
-
"offline-chat": {
|
|
159
|
-
model: PERPLEXITY_MODELS.LLAMA_3_1_70B,
|
|
160
|
-
reason: "Powerful offline model for complex conversations",
|
|
161
|
-
},
|
|
162
|
-
chat: {
|
|
163
|
-
model: DEFAULT_CHAT_MODEL,
|
|
164
|
-
reason: "Balanced performance for general chat",
|
|
165
|
-
},
|
|
166
|
-
streaming: {
|
|
167
|
-
model: DEFAULT_CHAT_MODEL,
|
|
168
|
-
reason: "Optimized for real-time streaming responses",
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
return recommendations[useCase] || recommendations.chat;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Get performance metrics for a model (V2 Enhanced)
|
|
177
|
-
* @param model Model name
|
|
178
|
-
* @returns Performance metrics
|
|
179
|
-
*/
|
|
180
|
-
getModelPerformance(model: string) {
|
|
181
|
-
const capabilities = MODEL_CAPABILITIES[model];
|
|
182
|
-
if (!capabilities) {
|
|
183
|
-
throw new Error(`Model "${model}" is not supported`);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
model,
|
|
188
|
-
contextWindow: capabilities.contextWindow,
|
|
189
|
-
online: capabilities.online,
|
|
190
|
-
estimatedLatency: capabilities.online ? "2-4s" : "1-2s",
|
|
191
|
-
costTier: model.includes("sonar") ? "premium" : "standard",
|
|
192
|
-
version: "v2",
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
private normalizeMessages(
|
|
197
|
-
messages: string[] | Array<{ role: string; content: string }>
|
|
198
|
-
): IPerplexityMessage[] {
|
|
199
|
-
if (Array.isArray(messages) && typeof messages[0] === "string") {
|
|
200
|
-
// Convert string array to message objects
|
|
201
|
-
return (messages as string[]).map((content, index) => ({
|
|
202
|
-
role: index === 0 ? "user" : "assistant",
|
|
203
|
-
content,
|
|
204
|
-
})) as IPerplexityMessage[];
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return messages as IPerplexityMessage[];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
private validateModel(model: string): void {
|
|
211
|
-
if (!models.includes(model)) {
|
|
212
|
-
throw new Error(
|
|
213
|
-
`Model "${model}" is not supported. Available models: ${models.join(
|
|
214
|
-
", "
|
|
215
|
-
)}`
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import OpenAI from "openai";
|
|
2
|
-
import { logger } from "../models/Logger";
|
|
3
|
-
import { Metering } from "../models/Metering";
|
|
4
|
-
import { generateTransactionId } from "../utils/generateTransactionId";
|
|
5
|
-
import { IOperationType } from "../interfaces/operation";
|
|
6
|
-
import { IUsageMetadata } from "../interfaces/usageMetadata";
|
|
7
|
-
import { ITokenCounts } from "../interfaces/tokenCounts";
|
|
8
|
-
import {
|
|
9
|
-
IChatCompletionRequest,
|
|
10
|
-
IPerplexityMessage,
|
|
11
|
-
} from "../interfaces/chatCompletionRequest";
|
|
12
|
-
import {
|
|
13
|
-
IPerplexityV2Response,
|
|
14
|
-
IPerplexityRawResponse,
|
|
15
|
-
} from "../interfaces/perplexityResponse";
|
|
16
|
-
import {
|
|
17
|
-
IPerplexityV2StreamingResponse,
|
|
18
|
-
IPerplexityV2StreamChunk,
|
|
19
|
-
} from "../interfaces/perplexityStreaming";
|
|
20
|
-
import {
|
|
21
|
-
PERPLEXITY_API_BASE_URL,
|
|
22
|
-
PERPLEXITY_API_KEY,
|
|
23
|
-
PERPLEXITY_CLIENT_INITIALIZED_MESSAGE,
|
|
24
|
-
REVENIUM_METERING_API_KEY,
|
|
25
|
-
REVENIUM_METERING_BASE_URL,
|
|
26
|
-
} from "../utils";
|
|
27
|
-
|
|
28
|
-
export class PerplexityV2Service {
|
|
29
|
-
private client: OpenAI;
|
|
30
|
-
private working: boolean = true;
|
|
31
|
-
|
|
32
|
-
constructor() {
|
|
33
|
-
this.client = new OpenAI({
|
|
34
|
-
apiKey: PERPLEXITY_API_KEY,
|
|
35
|
-
baseURL: PERPLEXITY_API_BASE_URL,
|
|
36
|
-
});
|
|
37
|
-
logger.info(`${PERPLEXITY_CLIENT_INITIALIZED_MESSAGE} (V2)`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async createChatCompletion(
|
|
41
|
-
params: IChatCompletionRequest,
|
|
42
|
-
model: string
|
|
43
|
-
): Promise<IPerplexityV2Response> {
|
|
44
|
-
const transactionId = generateTransactionId();
|
|
45
|
-
const startTime = Date.now();
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const { usageMetadata, ...openaiParams } = params;
|
|
49
|
-
const requestParams = {
|
|
50
|
-
...openaiParams,
|
|
51
|
-
model,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const result = (await this.client.chat.completions.create({
|
|
55
|
-
...requestParams,
|
|
56
|
-
stream: false,
|
|
57
|
-
})) as IPerplexityRawResponse;
|
|
58
|
-
|
|
59
|
-
const endTime = Date.now();
|
|
60
|
-
const processingTime = endTime - startTime;
|
|
61
|
-
|
|
62
|
-
// Build V2 enhanced response format (following OpenAI Responses API)
|
|
63
|
-
const perplexityV2Response: IPerplexityV2Response = {
|
|
64
|
-
id: result.id,
|
|
65
|
-
object: "chat.completion",
|
|
66
|
-
created: result.created,
|
|
67
|
-
model: result.model,
|
|
68
|
-
choices: result.choices,
|
|
69
|
-
usage: {
|
|
70
|
-
prompt_tokens: result.usage?.prompt_tokens || 0,
|
|
71
|
-
completion_tokens: result.usage?.completion_tokens || 0,
|
|
72
|
-
total_tokens: result.usage?.total_tokens || 0,
|
|
73
|
-
},
|
|
74
|
-
metadata: {
|
|
75
|
-
transactionId,
|
|
76
|
-
processingTime,
|
|
77
|
-
version: "v2",
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// Send metering data (modernized approach)
|
|
82
|
-
await this.sendMeteringData(
|
|
83
|
-
transactionId,
|
|
84
|
-
model,
|
|
85
|
-
IOperationType.CHAT,
|
|
86
|
-
{
|
|
87
|
-
inputTokenCount: perplexityV2Response.usage.prompt_tokens,
|
|
88
|
-
outputTokenCount: perplexityV2Response.usage.completion_tokens,
|
|
89
|
-
totalTokenCount: perplexityV2Response.usage.total_tokens,
|
|
90
|
-
},
|
|
91
|
-
processingTime,
|
|
92
|
-
usageMetadata
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
return perplexityV2Response;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
logger.error("Error in Perplexity V2 chat completion:", error);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async createStreamingCompletion(
|
|
103
|
-
params: IChatCompletionRequest,
|
|
104
|
-
model: string
|
|
105
|
-
): Promise<IPerplexityV2StreamingResponse> {
|
|
106
|
-
const transactionId = generateTransactionId();
|
|
107
|
-
const startTime = Date.now();
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const { usageMetadata, ...openaiParams } = params;
|
|
111
|
-
const requestParams = {
|
|
112
|
-
...openaiParams,
|
|
113
|
-
model,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const stream = await this.client.chat.completions.create({
|
|
117
|
-
...requestParams,
|
|
118
|
-
stream: true,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const wrappedStream = this.wrapStreamV2(
|
|
122
|
-
stream as any,
|
|
123
|
-
transactionId,
|
|
124
|
-
model,
|
|
125
|
-
startTime,
|
|
126
|
-
usageMetadata
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
id: transactionId,
|
|
131
|
-
object: "chat.completion.chunk",
|
|
132
|
-
stream: wrappedStream,
|
|
133
|
-
metadata: {
|
|
134
|
-
transactionId,
|
|
135
|
-
startTime,
|
|
136
|
-
version: "v2",
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.error("Error in Perplexity V2 streaming:", error);
|
|
141
|
-
throw error;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private async *wrapStreamV2(
|
|
146
|
-
stream: AsyncIterable<any>,
|
|
147
|
-
transactionId: string,
|
|
148
|
-
model: string,
|
|
149
|
-
startTime: number,
|
|
150
|
-
usageMetadata?: IUsageMetadata
|
|
151
|
-
): AsyncGenerator<IPerplexityV2StreamChunk> {
|
|
152
|
-
let lastChunk: any = null;
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
for await (const chunk of stream) {
|
|
156
|
-
lastChunk = chunk;
|
|
157
|
-
|
|
158
|
-
// Transform to V2 format (following OpenAI Responses API)
|
|
159
|
-
const v2Chunk: IPerplexityV2StreamChunk = {
|
|
160
|
-
id: chunk.id,
|
|
161
|
-
object: "chat.completion.chunk",
|
|
162
|
-
created: chunk.created,
|
|
163
|
-
model: chunk.model,
|
|
164
|
-
choices: chunk.choices,
|
|
165
|
-
usage: chunk.usage
|
|
166
|
-
? {
|
|
167
|
-
prompt_tokens: chunk.usage.prompt_tokens || 0,
|
|
168
|
-
completion_tokens: chunk.usage.completion_tokens || 0,
|
|
169
|
-
total_tokens: chunk.usage.total_tokens || 0,
|
|
170
|
-
}
|
|
171
|
-
: undefined,
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
yield v2Chunk;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Send metering data when stream completes
|
|
178
|
-
const endTime = Date.now();
|
|
179
|
-
const processingTime = endTime - startTime;
|
|
180
|
-
|
|
181
|
-
if (lastChunk?.usage) {
|
|
182
|
-
await this.sendMeteringData(
|
|
183
|
-
transactionId,
|
|
184
|
-
model,
|
|
185
|
-
IOperationType.CHAT,
|
|
186
|
-
{
|
|
187
|
-
inputTokenCount: lastChunk.usage.prompt_tokens || 0,
|
|
188
|
-
outputTokenCount: lastChunk.usage.completion_tokens || 0,
|
|
189
|
-
totalTokenCount: lastChunk.usage.total_tokens || 0,
|
|
190
|
-
},
|
|
191
|
-
processingTime,
|
|
192
|
-
usageMetadata
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
} catch (error) {
|
|
196
|
-
logger.error("Error in Perplexity V2 stream processing:", error);
|
|
197
|
-
throw error;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private async sendMeteringData(
|
|
202
|
-
transactionId: string,
|
|
203
|
-
model: string,
|
|
204
|
-
operationType: IOperationType,
|
|
205
|
-
usageData: IUsageMetadata,
|
|
206
|
-
processingTime: number,
|
|
207
|
-
customMetadata?: IUsageMetadata
|
|
208
|
-
): Promise<void> {
|
|
209
|
-
if (!this.working) {
|
|
210
|
-
logger.warning("Metering is not working. Check your configuration.");
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
const startTime = new Date(Date.now() - processingTime);
|
|
216
|
-
const endTime = new Date();
|
|
217
|
-
|
|
218
|
-
const metering = new Metering(
|
|
219
|
-
REVENIUM_METERING_API_KEY ?? "",
|
|
220
|
-
REVENIUM_METERING_BASE_URL ?? ""
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
// Convert IUsageMetadata to ITokenCounts for metering
|
|
224
|
-
const tokenCounts: ITokenCounts = {
|
|
225
|
-
inputTokens: usageData.inputTokenCount || 0,
|
|
226
|
-
outputTokens: usageData.outputTokenCount || 0,
|
|
227
|
-
totalTokens: usageData.totalTokenCount || 0,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// Combine custom metadata with transaction ID (token counts are handled separately)
|
|
231
|
-
const combinedMetadata: IUsageMetadata = {
|
|
232
|
-
...customMetadata,
|
|
233
|
-
transactionId,
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
// Use the same approach as V1 but with V2 enhancements
|
|
237
|
-
const meteringData = metering.createMetering(
|
|
238
|
-
{
|
|
239
|
-
modelName: model,
|
|
240
|
-
endTime,
|
|
241
|
-
startTime,
|
|
242
|
-
operationType,
|
|
243
|
-
stopReason: "END",
|
|
244
|
-
tokenCounts,
|
|
245
|
-
usageMetadata: combinedMetadata,
|
|
246
|
-
},
|
|
247
|
-
false
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
await metering.sendMeteringData(meteringData);
|
|
251
|
-
logger.info("V2 Metering data sent successfully.");
|
|
252
|
-
} catch (error) {
|
|
253
|
-
logger.error("Error sending V2 metering data:", error);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
setWorking(working: boolean) {
|
|
258
|
-
this.working = working;
|
|
259
|
-
}
|
|
260
|
-
}
|