yxmiler commited on
Commit
314abaa
·
verified ·
1 Parent(s): 074a9bd

Upload index.js

Browse files
Files changed (1) hide show
  1. index.js +474 -0
index.js ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fetch from 'node-fetch';
2
+ import express from 'express';
3
+ import cors from 'cors';
4
+ import dotenv from 'dotenv';
5
+
6
+ dotenv.config();
7
+
8
+ const Tokens =[];
9
+ let tokenManager;
10
+ const CONFIG = {
11
+ API: {
12
+ BASE_URL: "https://partyrock.aws",
13
+ API_KEY: process.env.API_KEY || "sk-123456"//自定义你自己的认证密钥,记得修改
14
+ },
15
+ RETRY: {
16
+ MAX_ATTEMPTS: 1,
17
+ DELAY_BASE: 1000
18
+ },
19
+ SERVER: {
20
+ PORT: process.env.PORT || 3000,
21
+ BODY_LIMIT: '5mb'
22
+ },
23
+ MODELS: {
24
+ 'claude-3-5-haiku-20241022': 'bedrock-anthropic.claude-3-5-haiku',
25
+ 'claude-3-5-sonnet-20241022': 'bedrock-anthropic.claude-3-5-sonnet-v2-0',
26
+ 'nova-lite-v1-0': 'bedrock-amazon.nova-lite-v1-0',
27
+ 'nova-pro-v1-0': 'bedrock-amazon.nova-pro-v1-0',
28
+ 'llama3-1-7b': 'bedrock-meta.llama3-1-8b-instruct-v1',
29
+ 'llama3-1-70b': 'bedrock-meta.llama3-1-70b-instruct-v1',
30
+ 'mistral-small': 'bedrock-mistral.mistral-small-2402-v1-0',
31
+ 'mistral-large': 'bedrock-mistral.mistral-large-2407-v1-0'
32
+ },
33
+ DEFAULT_HEADERS: {
34
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
35
+ "Connection": "keep-alive",
36
+ "Accept": "text/event-stream",
37
+ "Accept-Encoding": "gzip, deflate, br, zstd",
38
+ "Content-Type": "application/json",
39
+ "anti-csrftoken-a2z": "",
40
+ "sec-ch-ua-mobile": "?0",
41
+ "origin": "https://partyrock.aws",
42
+ "sec-fetch-site": "same-origin",
43
+ "sec-fetch-mode": "cors",
44
+ "sec-fetch-dest": "empty",
45
+ "sec-ch-ua-platform": "\"Windows\"",
46
+ "sec-ch-ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
47
+ "referer": "",
48
+ "Cookie": "",
49
+ "accept-language": "zh-CN,zh;q=0.9",
50
+ "priority": "u=1, i"
51
+ }
52
+ };
53
+
54
+ class TokenManager {
55
+ constructor() {
56
+ this.cache = {
57
+ currentIndex: 0,
58
+ tokens: {}
59
+ };
60
+ }
61
+
62
+ async getCurrentTokenIndex() {
63
+ return this.cache.currentIndex;
64
+ }
65
+
66
+ async updateTokenIndex() {
67
+ const nextIndex = (this.cache.currentIndex + 1) % Tokens.length;
68
+ this.cache.currentIndex = nextIndex;
69
+ }
70
+
71
+ async getCachedTokens() {
72
+ const currentIndex = await this.getCurrentTokenIndex();
73
+ const csrfToken = this.cache.tokens.csrfToken
74
+ ? this.cache.tokens.csrfToken
75
+ : Tokens[currentIndex]["anti_csrftoken_a2z"];
76
+
77
+ const idToken = this.cache.tokens.idToken
78
+ ? this.cache.tokens.idToken
79
+ : Tokens[currentIndex]["idToken"];
80
+ return {
81
+ csrfToken: csrfToken,
82
+ idToken: idToken,
83
+ pr_refresh_token: Tokens[currentIndex]["pr_refresh_token"],
84
+ aws_waf_token: Tokens[currentIndex]["aws_waf_token"],
85
+ refreshUrl: Tokens[currentIndex]["refreshUrl"]
86
+ };
87
+ }
88
+
89
+ async updateTokens(response) {
90
+ const newCsrfToken = response.headers.get('anti-csrftoken-a2z');
91
+ if (newCsrfToken) {
92
+ this.cache.tokens.csrfToken = newCsrfToken;
93
+ }
94
+
95
+ const cookies = response.headers.get('set-cookie');
96
+ if (cookies) {
97
+ const idTokenMatch = cookies.match(/idToken=([^;]+)/);
98
+ if (idTokenMatch && idTokenMatch[1]) {
99
+ this.cache.tokens.idToken = idTokenMatch[1];
100
+ }
101
+ }
102
+
103
+ await this.updateTokenIndex();
104
+ }
105
+ }
106
+ class Utils {
107
+ static async extractTokens(cookieString) {
108
+ const tokens = {};
109
+ const cookiePairs = cookieString.split(';').map(pair => pair.trim());
110
+
111
+ cookiePairs.forEach(pair => {
112
+ const splitIndex = pair.indexOf('=');
113
+ const key = pair.slice(0, splitIndex).trim();
114
+ const value = pair.slice(splitIndex + 1).trim();
115
+
116
+ tokens[key] = value;
117
+ });
118
+
119
+ return tokens;
120
+ }
121
+ // 获取数组中的随机元素
122
+ static getRandomElement(arr) {
123
+ return arr[Math.floor(Math.random() * arr.length)];
124
+ }
125
+
126
+ // 生成UUID
127
+ static uuidv4() {
128
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
129
+ const r = (Math.random() * 16) | 0;
130
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
131
+ return v.toString(16);
132
+ });
133
+ }
134
+
135
+ // 生成随机十六进制字符串
136
+ static generateRandomHexString(length) {
137
+ let result = '';
138
+ const characters = '0123456789ABCDEF';
139
+ for (let i = 0; i < length; i++) {
140
+ result += characters.charAt(Math.floor(Math.random() * characters.length));
141
+ }
142
+ return result;
143
+ }
144
+ }
145
+ async function initializeService() {
146
+ console.log('服务初始化中...');
147
+ // 遍历所有可能的 token 组
148
+ let index = 0;
149
+ while (true) {
150
+ const refreshUrl = process.env[`AUTH_TOKENS_${index}_REFRESH_URL`];
151
+ const anti_csrftoken_a2z = process.env[`AUTH_TOKENS_${index}_ANTI_CSRF_TOKEN`];
152
+ const cookie = process.env[`AUTH_TOKENS_${index}_COOKIE`];
153
+ if (!refreshUrl && !anti_csrftoken_a2z && !cookie) {
154
+ break;
155
+ }
156
+ const cookies = await Utils.extractTokens(cookie);
157
+ // 只有当所有属性都存在时才添加 token
158
+ if (refreshUrl && anti_csrftoken_a2z && cookie) {
159
+ Tokens.push({
160
+ refreshUrl,
161
+ anti_csrftoken_a2z,
162
+ pr_refresh_token: cookies["pr_refresh_token"],
163
+ aws_waf_token: cookies["aws-waf-token"],
164
+ idToken: cookies["idToken"]
165
+ });
166
+ }
167
+ index++;
168
+ }
169
+ tokenManager = new TokenManager();
170
+ }
171
+
172
+ await initializeService();
173
+
174
+
175
+
176
+ class ApiClient {
177
+ constructor(modelId) {
178
+ console.log(modelId);
179
+ if (!CONFIG.MODELS[modelId]) {
180
+ throw new Error(`不支持的模型: ${modelId}`);
181
+ }
182
+ this.modelId = CONFIG.MODELS[modelId];
183
+ }
184
+
185
+ processMessageContent(content) {
186
+ if (typeof content === 'string') return content;
187
+
188
+ if (Array.isArray(content)) {
189
+ return content
190
+ .map(item => item.text)
191
+ .join('\n');
192
+ }
193
+
194
+ if (typeof content === 'object') return content.text || null;
195
+ return null;
196
+ }
197
+
198
+ //合并相同role的消息
199
+ async transformMessages(request) {
200
+ const mergedMessages = await request.messages.reduce(async (accPromise, current) => {
201
+ const acc = await accPromise;
202
+ const lastMessage = acc[acc.length - 1];
203
+ if (lastMessage && lastMessage.role == "system") {
204
+ lastMessage.role = "user"
205
+ }
206
+ if (current && current.role == "system") {
207
+ current.role = "user"
208
+ }
209
+ const currentContent = this.processMessageContent(current.content);
210
+
211
+ if (currentContent === null) return acc;
212
+
213
+ if (lastMessage && current && (lastMessage.role == current.role)) {
214
+ const lastContent = this.processMessageContent(lastMessage.content);
215
+ if (lastContent !== null) {
216
+ lastMessage.content = [
217
+ {
218
+ "text": `${lastContent}\r\n${currentContent}`
219
+ }
220
+ ];
221
+ return acc;
222
+ }
223
+ }
224
+ current.content = [
225
+ {
226
+ "text": currentContent
227
+ }
228
+ ]
229
+ acc.push(current);
230
+ return acc;
231
+ }, Promise.resolve([]));
232
+ // 处理请求参数
233
+ let topP = request.top_p || 0.5;
234
+ let temperature = request.temperature || 0.95;
235
+ if (topP >= 1) {
236
+ topP = 1;
237
+ }
238
+ if (temperature >= 1) {
239
+ temperature = 1;
240
+ }
241
+ const extractPartyRockId = url => url.match(/https:\/\/partyrock\.aws\/u\/[^/]+\/([^/]+)/)?.[1];
242
+ console.log(CONFIG.DEFAULT_HEADERS.referer);
243
+
244
+ const requestPayload = {
245
+ "messages": mergedMessages,
246
+ "modelName": this.modelId,
247
+ "context": {
248
+ "type": "chat-widget",
249
+ "appId": extractPartyRockId(CONFIG.DEFAULT_HEADERS.referer)
250
+ },
251
+ "options": {
252
+ "temperature": temperature,
253
+ "topP": topP
254
+ },
255
+ "apiVersion": 3
256
+ }
257
+ console.log(JSON.stringify(requestPayload, null, 2));
258
+ return requestPayload;
259
+ }
260
+ }
261
+ class MessageProcessor {
262
+ static createChatResponse(message, model, isStream = false) {
263
+ const baseResponse = {
264
+ id: `chatcmpl-${Utils.uuidv4()}`,
265
+ created: Math.floor(Date.now() / 1000),
266
+ model: model
267
+ };
268
+
269
+ if (isStream) {
270
+ return {
271
+ ...baseResponse,
272
+ object: 'chat.completion.chunk',
273
+ choices: [{
274
+ index: 0,
275
+ delta: { content: message }
276
+ }]
277
+ };
278
+ }
279
+
280
+ return {
281
+ ...baseResponse,
282
+ object: 'chat.completion',
283
+ choices: [{
284
+ index: 0,
285
+ message: {
286
+ role: 'assistant',
287
+ content: message
288
+ },
289
+ finish_reason: 'stop'
290
+ }],
291
+ usage: null
292
+ };
293
+ }
294
+ }
295
+ class ResponseHandler {
296
+ static async handleStreamResponse(response, model, res) {
297
+ res.setHeader('Content-Type', 'text/event-stream');
298
+ res.setHeader('Cache-Control', 'no-cache');
299
+ res.setHeader('Connection', 'keep-alive');
300
+
301
+ try {
302
+ const stream = response.body;
303
+ let buffer = '';
304
+ let decoder = new TextDecoder('utf-8');
305
+ stream.on('data', (chunk) => {
306
+ buffer += decoder.decode(chunk, { stream: true });
307
+ const lines = buffer.split('\n');
308
+ buffer = lines.pop() || '';
309
+
310
+ for (const line of lines) {
311
+ if (!line) continue;
312
+ const trimmedLine = line.trim();
313
+ if (trimmedLine && trimmedLine.startsWith('data: ')) {
314
+ const data = trimmedLine.substring(6);
315
+ if (!data) continue;
316
+ try {
317
+ const json = JSON.parse(data);
318
+ if (json?.text) {
319
+ var content = json.text;
320
+ console.log(content);
321
+ const responseData = MessageProcessor.createChatResponse(content, model, true);
322
+ res.write(`data: ${JSON.stringify(responseData)}\n\n`);
323
+ }
324
+ } catch (error) {
325
+ console.error('JSON解析错误:', error);
326
+ }
327
+ }
328
+ }
329
+ });
330
+ stream.on('end', () => {
331
+ res.write('data: [DONE]\n\n');
332
+ res.end();
333
+ });
334
+ stream.on('error', (error) => {
335
+ console.error('流处理错误:', error);
336
+ res.write('data: [DONE]\n\n');
337
+ res.end();
338
+ });
339
+
340
+ } catch (error) {
341
+ console.error('处理响应错误:', error);
342
+ res.write('data: [DONE]\n\n');
343
+ res.end();
344
+ }
345
+ }
346
+
347
+ static async handleNormalResponse(response, model, res) {
348
+ const text = await response.text();
349
+ const lines = text.split("\n");
350
+ let fullResponse = '';
351
+
352
+ for (let line of lines) {
353
+ line = line.trim();
354
+ if (line) {
355
+ if (line.startsWith('data: ')) {
356
+ let data = line.substring(6);
357
+ if (data === '[DONE]') break;
358
+ try {
359
+ let json = JSON.parse(data)
360
+ if (json?.text) {
361
+ fullResponse += json.text;
362
+ }
363
+ } catch (error) {
364
+ console.log("json解析错误");
365
+ continue
366
+ }
367
+ }
368
+ }
369
+ }
370
+ const responseData = MessageProcessor.createChatResponse(fullResponse, model);
371
+ res.json(responseData);
372
+ }
373
+ }
374
+ // Express 应用设置
375
+ const app = express();
376
+ app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
377
+ app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
378
+
379
+ app.use(cors({
380
+ origin: '*',
381
+ methods: ['GET', 'POST', 'OPTIONS'],
382
+ allowedHeaders: ['*']
383
+ }));
384
+ // 路由处理
385
+ app.get('/v1/models', (req, res) => {
386
+ res.json({
387
+ object: "list",
388
+ data: Object.keys(CONFIG.MODELS).map((model, index) => ({
389
+ id: model,
390
+ object: "model",
391
+ created: Math.floor(Date.now() / 1000),
392
+ owned_by: "partyrock",
393
+ }))
394
+ });
395
+ });
396
+
397
+ app.post('/v1/chat/completions', async (req, res) => {
398
+ try {
399
+ const authToken = req.headers.authorization?.replace('Bearer ', '');
400
+ if (authToken !== CONFIG.API.API_KEY) {
401
+ return res.status(401).json({ error: "Unauthorized" });
402
+ }
403
+ const { csrfToken, idToken, pr_refresh_token, aws_waf_token, refreshUrl } = await tokenManager.getCachedTokens();
404
+ CONFIG.DEFAULT_HEADERS["anti-csrftoken-a2z"] = csrfToken;
405
+ CONFIG.DEFAULT_HEADERS.Cookie = `idToken=${idToken}; pr_refresh_token=${pr_refresh_token};aws-waf-token=${aws_waf_token}`;
406
+ CONFIG.DEFAULT_HEADERS.referer = refreshUrl;
407
+ console.log(JSON.stringify(CONFIG.DEFAULT_HEADERS, null, 2));
408
+
409
+ const apiClient = new ApiClient(req.body.model);
410
+ const requestPayload = await apiClient.transformMessages(req.body);
411
+ let retryCount = 0;
412
+
413
+ while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
414
+ try {
415
+ console.log("开始请求");
416
+ //发送请求
417
+ var response = await fetch(`https://partyrock.aws/stream/getCompletion`, {
418
+ method: "POST",
419
+ headers: {
420
+ ...CONFIG.DEFAULT_HEADERS
421
+ },
422
+ body: JSON.stringify(requestPayload)
423
+ });
424
+ await tokenManager.updateTokens(response);
425
+ if (response.status == 200) {
426
+ console.log("请求成功");
427
+ break; // 如果请求成功,跳出重试循环
428
+ }
429
+ if (response.status != 200) {
430
+ console.error(JSON.stringify(await response.text(), null, 2));
431
+ }
432
+ retryCount++;
433
+ if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
434
+ throw new Error(`上游服务请求失败! status: ${response.status}`);
435
+ }
436
+ // 等待一段时间后重试
437
+ await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY.DELAY_BASE * retryCount));
438
+
439
+ } catch (error) {
440
+ retryCount++;
441
+ if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
442
+ throw error;
443
+ }
444
+ // 等待一段时间后重试
445
+ await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY.DELAY_BASE * retryCount));
446
+ }
447
+ }
448
+ if (req.body.stream) {
449
+ await ResponseHandler.handleStreamResponse(response, req.body.model, res);
450
+ } else {
451
+ await ResponseHandler.handleNormalResponse(response, req.body.model, res);
452
+ }
453
+
454
+ } catch (error) {
455
+ res.status(500).json({
456
+ error: {
457
+ message: error.message,
458
+ type: 'server_error',
459
+ param: null,
460
+ code: error.code || null
461
+ }
462
+ });
463
+ }
464
+ });
465
+
466
+
467
+ app.use((req, res) => {
468
+ res.status(404).json({ message: "API服务运行正常" });
469
+ });
470
+
471
+ // 启动服务器
472
+ app.listen(CONFIG.SERVER.PORT, () => {
473
+ console.log(`服务器运行在端口 ${CONFIG.SERVER.PORT} `);
474
+ });