yxmiler commited on
Commit
8b4142b
·
verified ·
1 Parent(s): 7e3c652

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +199 -202
index.js CHANGED
@@ -10,165 +10,155 @@ dotenv.config();
10
 
11
  // 配置常量
12
  const CONFIG = {
13
- API: {
14
- BASE_URL: "wss://api.inkeep.com/graphql",
15
- API_KEY: process.env.API_KEY || "sk-123456",
16
- SYSTEM_MESSAGE: process.env.SYSTEM_MESSAGE || null
17
- },
18
- MODELS: {
19
- 'claude-3-5-sonnet-20241022': 'claude-3-5-sonnet-20241022',
20
- },
21
- SERVER: {
22
- PORT: process.env.PORT || 3000,
23
- BODY_LIMIT: '5mb'
24
- },
25
- DEFAULT_HEADERS: {
26
- 'Host': 'api.inkeep.com',
27
- 'Connection': 'Upgrade',
28
- 'Pragma': 'no-cache',
29
- 'Cache-Control': 'no-cache',
30
- 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.72 Safari/537.36',
31
- 'Upgrade': 'websocket',
32
- 'Origin': 'https://docs.anthropic.com',
33
- 'Sec-WebSocket-Version': '13',
34
- 'Accept-Encoding': 'gzip, deflate, br, zstd',
35
- 'Accept-Language': 'zh-CN,zh;q=0.9',
36
- 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
37
- 'Sec-WebSocket-Protocol': 'graphql-transport-ws'
38
- }
39
  };
40
 
41
  // AI API 客户端类
42
  class AiApiClient {
43
- constructor(modelId) {
44
- this.modelId = CONFIG.MODELS[modelId];
45
- if (!this.modelId) {
46
- throw new Error(`不支持的模型: ${modelId}`);
47
- }
48
- }
49
-
50
- // 处理消息内容
51
- processMessageContent(content) {
52
- if (typeof content === 'string') return content;
53
- if (Array.isArray(content)) {
54
- return content
55
- .filter(item => item.type === 'text')
56
- .map(item => item.text)
57
- .join('\n');
58
- }
59
- return typeof content === 'object' ? content.text || null : null;
60
- }
61
-
62
- // 转换消息格式
63
- async transformMessages(request) {
64
- let systemMessageList = [];
65
- let systemMergeMode = false;
66
- let closedSystemMergeMode = false;
67
-
68
- const contextMessages = await request.messages.reduce(async (accPromise, current) => {
69
- const acc = await accPromise;
70
- const currentContent = this.processMessageContent(current.content);
71
-
72
- if (currentContent === null) return acc;
73
-
74
- const currentMessageRole = current.role === "system" || current.role === "user" ? "Human" : "Assistant";
75
-
76
- // 系统消息处理逻辑
77
- if (current.role === "system") {
78
- if (!closedSystemMergeMode) {
79
- systemMergeMode = true;
80
- const lastSystemMessage = systemMessageList[systemMessageList.length - 1];
81
-
82
- if (!lastSystemMessage) {
83
- systemMessageList.push(currentContent);
84
- } else {
85
- systemMessageList[systemMessageList.length - 1] = `${lastSystemMessage}\n${currentContent}`;
86
- }
87
- return acc;
88
- }
89
- }
90
-
91
- // 关闭系统消息合并模式
92
- if (current.role !== "system" && systemMergeMode) {
93
- systemMergeMode = false;
94
- closedSystemMergeMode = true;
95
- }
96
-
97
- // 消息合并逻辑
98
- const previousMessage = acc[acc.length - 1];
99
- const newMessage = `${currentMessageRole}: ${currentContent}`;
100
-
101
- if (!previousMessage || previousMessage.startsWith(currentMessageRole)) {
102
- return previousMessage
103
- ? [...acc.slice(0, -1), `${previousMessage}\n${currentContent}`]
104
- : [...acc, newMessage];
105
- }
106
-
107
- return [...acc, newMessage];
108
- }, Promise.resolve([]));
109
-
110
- return {
111
- contextMessages: contextMessages.join('\n'),
112
- systemMessage: systemMessageList.join('\n')
113
- };
114
- }
115
  }
116
 
117
  // 响应处理类
118
  class ResponseHandler {
119
- // 流式响应处理
120
- static async handleStreamResponse(responseContent, model, res) {
121
- res.setHeader('Content-Type', 'text/event-stream');
122
- res.setHeader('Cache-Control', 'no-cache');
123
- res.setHeader('Connection', 'keep-alive');
124
-
125
- let index = 0;
126
- while (index < responseContent.length) {
127
- const chunkSize = Math.floor(Math.random() * (30 - 16)) + 15;
128
- const chunk = responseContent.slice(index, index + chunkSize);
129
-
130
- res.write(`data: ${JSON.stringify({
131
- id: uuidv4(),
132
- object: 'chat.completion.chunk',
133
- created: Math.floor(Date.now() / 1000),
134
- model: model,
135
- choices: [{
136
- index: 0,
137
- delta: { content: chunk },
138
- finish_reason: null
139
- }]
140
- })}\n\n`);
141
-
142
- index += chunkSize;
143
- await new Promise(resolve => setTimeout(resolve, 50));
144
- }
145
-
146
- res.write('data: [DONE]\n\n');
147
- res.end();
148
- }
149
-
150
- // 普通响应处理
151
- static async handleNormalResponse(userMessage, responseContent, model, res) {
152
- res.json({
153
- id: uuidv4(),
154
- object: "chat.completion",
155
- created: Math.floor(Date.now() / 1000),
156
- model: model,
157
- choices: [{
158
- index: 0,
159
- message: {
160
- role: "assistant",
161
- content: responseContent
162
- },
163
- finish_reason: "stop"
164
- }],
165
- usage: {
166
- prompt_tokens: userMessage.length,
167
- completion_tokens: responseContent.length,
168
- total_tokens: userMessage.length + responseContent.length
169
- }
170
- });
171
- }
172
  }
173
 
174
  // WebSocket工具类
@@ -181,9 +171,11 @@ class WebSocketUtils {
181
  static generateWebSocketKey() {
182
  return randomBytes(16).toString('base64');
183
  }
184
-
 
 
185
  // 创建WebSocket客户端
186
- static async createWebSocketClient(requestPayload) {
187
  // 检查当前连接数是否达到上限
188
  if (this.activeConnections.size >= this.MAX_CONNECTIONS) {
189
  throw new Error(`当前连接数已达到上限 (${this.MAX_CONNECTIONS}),请稍后重试!`);
@@ -217,6 +209,7 @@ class WebSocketUtils {
217
  }, this.TIMEOUT);
218
 
219
  let responseContent = '';
 
220
  let isComplete = false;
221
 
222
  ws.on('open', () => {
@@ -235,7 +228,7 @@ class WebSocketUtils {
235
  ws.on('message', async (data) => {
236
  const message = data.toString();
237
  const parsedMessage = JSON.parse(message);
238
-
239
  switch (parsedMessage.type) {
240
  case 'connection_ack':
241
  console.log('WebSocket连接请求中');
@@ -245,14 +238,22 @@ class WebSocketUtils {
245
  const chatResponse = await this.handleChatResponse(parsedMessage);
246
  if (chatResponse) {
247
  responseContent = chatResponse;
248
- // 获取到响应后立即关闭连接
249
- isComplete = true;
250
- ws.close();
251
- resolve(responseContent);
 
 
 
 
252
  }
253
  break;
254
  case 'complete':
255
  isComplete = true;
 
 
 
 
256
  ws.close();
257
  resolve(responseContent);
258
  break;
@@ -332,8 +333,6 @@ class WebSocketUtils {
332
  }`
333
  }
334
  };
335
-
336
- //console.log(JSON.stringify(subscribeMessage,null,2));
337
  if (ws.readyState === WebSocket.OPEN) {
338
  ws.send(JSON.stringify(subscribeMessage));
339
  }
@@ -343,7 +342,7 @@ class WebSocketUtils {
343
  static async handleChatResponse(message) {
344
  if (message.payload && message.payload.data) {
345
  const chatResult = message.payload.data.newSessionChatResult;
346
- if (chatResult && chatResult.isEnd == true && chatResult.message) {
347
  return chatResult.message.content;
348
  }
349
  }
@@ -363,62 +362,60 @@ const app = express();
363
  app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
364
  app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
365
  app.use(cors({
366
- origin: '*',
367
- methods: ['GET', 'POST', 'OPTIONS'],
368
- allowedHeaders: ['Content-Type', 'Authorization']
369
  }));
370
 
371
 
372
  // 获取模型列表路由
373
  app.get('/hf/v1/models', (req, res) => {
374
- res.json({
375
- object: "list",
376
- data: [{
377
- id: "claude-3-5-sonnet-20241022",
378
- object: "model",
379
- created: Math.floor(Date.now() / 1000),
380
- owned_by: "claude",
381
- }]
382
- });
383
  });
384
 
385
  // 聊天完成路由
386
  app.post('/hf/v1/chat/completions', async (req, res) => {
387
- try {
388
- const { messages, model, stream } = req.body;
389
- const authToken = req.headers.authorization?.replace('Bearer ', '');
390
-
391
- if (authToken !== CONFIG.API.API_KEY) {
392
- return res.status(401).json({ error: "Unauthorized" });
393
- }
394
-
395
- const apiClient = new AiApiClient(req.body.model);
396
- const requestPayload = await apiClient.transformMessages(req.body);
397
-
398
- const userMessage = messages.reverse().find(message => message.role === 'user')?.content;
399
- if (!userMessage) {
400
- return res.status(400).json({ error: "缺失用户消息" });
401
- }
402
-
403
- const responseContent = await WebSocketUtils.createWebSocketClient(requestPayload);
404
-
405
- if (stream) {
406
- await ResponseHandler.handleStreamResponse(responseContent, model, res);
407
- } else {
408
- await ResponseHandler.handleNormalResponse(userMessage, responseContent, model, res);
409
- }
410
- } catch (error) {
411
- console.error('处理请求时发生错误:', error);
412
- res.status(500).json({ error: "内部服务器错误,请查询日志记录!", details: error.message });
413
- }
414
  });
415
 
416
  // 404处理
417
  app.use((req, res) => {
418
- res.status(404).json({ message: "服务创建成功运行中,请根据规则使用正确请求路径" });
419
  });
420
 
421
  // 启动服务器
422
  app.listen(CONFIG.SERVER.PORT, () => {
423
- console.log(`服务器运行在 http://localhost:${CONFIG.SERVER.PORT}`);
424
  });
 
10
 
11
  // 配置常量
12
  const CONFIG = {
13
+ API: {
14
+ BASE_URL: "wss://api.inkeep.com/graphql",
15
+ API_KEY: process.env.API_KEY || "sk-123456",
16
+ SYSTEM_MESSAGE: process.env.SYSTEM_MESSAGE || null
17
+ },
18
+ MODELS: {
19
+ 'claude-3-5-sonnet-20241022': 'claude-3-5-sonnet-20241022',
20
+ },
21
+ SERVER: {
22
+ PORT: process.env.PORT || 3000,
23
+ BODY_LIMIT: '5mb'
24
+ },
25
+ DEFAULT_HEADERS: {
26
+ 'Host': 'api.inkeep.com',
27
+ 'Connection': 'Upgrade',
28
+ 'Pragma': 'no-cache',
29
+ 'Cache-Control': 'no-cache',
30
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.72 Safari/537.36',
31
+ 'Upgrade': 'websocket',
32
+ 'Origin': 'https://docs.anthropic.com',
33
+ 'Sec-WebSocket-Version': '13',
34
+ 'Accept-Encoding': 'gzip, deflate, br, zstd',
35
+ 'Accept-Language': 'zh-CN,zh;q=0.9',
36
+ 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
37
+ 'Sec-WebSocket-Protocol': 'graphql-transport-ws'
38
+ }
39
  };
40
 
41
  // AI API 客户端类
42
  class AiApiClient {
43
+ constructor(modelId) {
44
+ this.modelId = CONFIG.MODELS[modelId];
45
+ if (!this.modelId) {
46
+ throw new Error(`不支持的模型: ${modelId}`);
47
+ }
48
+ }
49
+
50
+ // 处理消息内容
51
+ processMessageContent(content) {
52
+ if (typeof content === 'string') return content;
53
+ if (Array.isArray(content)) {
54
+ return content
55
+ .filter(item => item.type === 'text')
56
+ .map(item => item.text)
57
+ .join('\n');
58
+ }
59
+ return typeof content === 'object' ? content.text || null : null;
60
+ }
61
+
62
+ // 转换消息格式
63
+ async transformMessages(request) {
64
+ let systemMessageList = [];
65
+ let systemMergeMode = false;
66
+ let closedSystemMergeMode = false;
67
+
68
+ const contextMessages = await request.messages.reduce(async (accPromise, current) => {
69
+ const acc = await accPromise;
70
+ const currentContent = this.processMessageContent(current.content);
71
+
72
+ if (currentContent === null) return acc;
73
+
74
+ const currentMessageRole = current.role === "system" || current.role === "user" ? "Human" : "Assistant";
75
+
76
+ // 系统消息处理逻辑
77
+ if (current.role === "system") {
78
+ if (!closedSystemMergeMode) {
79
+ systemMergeMode = true;
80
+ const lastSystemMessage = systemMessageList[systemMessageList.length - 1];
81
+
82
+ if (!lastSystemMessage) {
83
+ systemMessageList.push(currentContent);
84
+ } else {
85
+ systemMessageList[systemMessageList.length - 1] = `${lastSystemMessage}\n${currentContent}`;
86
+ }
87
+ return acc;
88
+ }
89
+ }
90
+
91
+ // 关闭系统消息合并模式
92
+ if (current.role !== "system" && systemMergeMode) {
93
+ systemMergeMode = false;
94
+ closedSystemMergeMode = true;
95
+ }
96
+
97
+ // 消息合并逻辑
98
+ const previousMessage = acc[acc.length - 1];
99
+ const newMessage = `${currentMessageRole}: ${currentContent}`;
100
+
101
+ if (!previousMessage || previousMessage.startsWith(currentMessageRole)) {
102
+ return previousMessage
103
+ ? [...acc.slice(0, -1), `${previousMessage}\n${currentContent}`]
104
+ : [...acc, newMessage];
105
+ }
106
+
107
+ return [...acc, newMessage];
108
+ }, Promise.resolve([]));
109
+
110
+ return {
111
+ contextMessages: contextMessages.join('\n'),
112
+ systemMessage: systemMessageList.join('\n')
113
+ };
114
+ }
115
  }
116
 
117
  // 响应处理类
118
  class ResponseHandler {
119
+ // 流式响应处理
120
+ static async handleStreamResponse(responseContent, model, res) {
121
+ if (!res.headersSent) {
122
+ res.setHeader('Content-Type', 'text/event-stream');
123
+ res.setHeader('Cache-Control', 'no-cache');
124
+ res.setHeader('Connection', 'keep-alive');
125
+ }
126
+
127
+ res.write(`data: ${JSON.stringify({
128
+ id: uuidv4(),
129
+ object: 'chat.completion.chunk',
130
+ created: Math.floor(Date.now() / 1000),
131
+ model: model,
132
+ choices: [{
133
+ index: 0,
134
+ delta: { content: responseContent },
135
+ finish_reason: null
136
+ }]
137
+ })}\n\n`);
138
+ }
139
+
140
+ // 普通响应处理
141
+ static async handleNormalResponse(userMessage, responseContent, model, res) {
142
+ res.json({
143
+ id: uuidv4(),
144
+ object: "chat.completion",
145
+ created: Math.floor(Date.now() / 1000),
146
+ model: model,
147
+ choices: [{
148
+ index: 0,
149
+ message: {
150
+ role: "assistant",
151
+ content: responseContent
152
+ },
153
+ finish_reason: "stop"
154
+ }],
155
+ usage: {
156
+ prompt_tokens: userMessage.length,
157
+ completion_tokens: responseContent.length,
158
+ total_tokens: userMessage.length + responseContent.length
159
+ }
160
+ });
161
+ }
 
 
 
 
 
 
 
 
 
 
162
  }
163
 
164
  // WebSocket工具类
 
171
  static generateWebSocketKey() {
172
  return randomBytes(16).toString('base64');
173
  }
174
+ static getMessageDiff(prevContent, newContent) {
175
+ return newContent.slice(prevContent.length);
176
+ }
177
  // 创建WebSocket客户端
178
+ static async createWebSocketClient(requestPayload, stream = false, res = null) {
179
  // 检查当前连接数是否达到上限
180
  if (this.activeConnections.size >= this.MAX_CONNECTIONS) {
181
  throw new Error(`当前连接数已达到上限 (${this.MAX_CONNECTIONS}),请稍后重试!`);
 
209
  }, this.TIMEOUT);
210
 
211
  let responseContent = '';
212
+ let prevContent = '';
213
  let isComplete = false;
214
 
215
  ws.on('open', () => {
 
228
  ws.on('message', async (data) => {
229
  const message = data.toString();
230
  const parsedMessage = JSON.parse(message);
231
+
232
  switch (parsedMessage.type) {
233
  case 'connection_ack':
234
  console.log('WebSocket连接请求中');
 
238
  const chatResponse = await this.handleChatResponse(parsedMessage);
239
  if (chatResponse) {
240
  responseContent = chatResponse;
241
+
242
+ if (stream && res) {
243
+ const diff = this.getMessageDiff(prevContent, responseContent);
244
+ if (diff) {
245
+ await ResponseHandler.handleStreamResponse(diff, "claude-3-5-sonnet-20241022", res);
246
+ prevContent = responseContent;
247
+ }
248
+ }
249
  }
250
  break;
251
  case 'complete':
252
  isComplete = true;
253
+ if (stream && res) {
254
+ res.write('data: [DONE]\n\n');
255
+ res.end();
256
+ }
257
  ws.close();
258
  resolve(responseContent);
259
  break;
 
333
  }`
334
  }
335
  };
 
 
336
  if (ws.readyState === WebSocket.OPEN) {
337
  ws.send(JSON.stringify(subscribeMessage));
338
  }
 
342
  static async handleChatResponse(message) {
343
  if (message.payload && message.payload.data) {
344
  const chatResult = message.payload.data.newSessionChatResult;
345
+ if (chatResult && chatResult.message) {
346
  return chatResult.message.content;
347
  }
348
  }
 
362
  app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
363
  app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
364
  app.use(cors({
365
+ origin: '*',
366
+ methods: ['GET', 'POST', 'OPTIONS'],
367
+ allowedHeaders: ['Content-Type', 'Authorization']
368
  }));
369
 
370
 
371
  // 获取模型列表路由
372
  app.get('/hf/v1/models', (req, res) => {
373
+ res.json({
374
+ object: "list",
375
+ data: [{
376
+ id: "claude-3-5-sonnet-20241022",
377
+ object: "model",
378
+ created: Math.floor(Date.now() / 1000),
379
+ owned_by: "claude",
380
+ }]
381
+ });
382
  });
383
 
384
  // 聊天完成路由
385
  app.post('/hf/v1/chat/completions', async (req, res) => {
386
+ try {
387
+ const { messages, model, stream } = req.body;
388
+ const authToken = req.headers.authorization?.replace('Bearer ', '');
389
+
390
+ if (authToken !== CONFIG.API.API_KEY) {
391
+ return res.status(401).json({ error: "Unauthorized" });
392
+ }
393
+
394
+ const apiClient = new AiApiClient(req.body.model);
395
+ const requestPayload = await apiClient.transformMessages(req.body);
396
+
397
+ const userMessage = messages.reverse().find(message => message.role === 'user')?.content;
398
+ if (!userMessage) {
399
+ return res.status(400).json({ error: "缺失用户消息" });
400
+ }
401
+
402
+ const responseContent = await WebSocketUtils.createWebSocketClient(requestPayload, stream, stream ? res : null);
403
+
404
+ if (!stream) {
405
+ await ResponseHandler.handleNormalResponse(userMessage, responseContent, model, res);
406
+ }
407
+ } catch (error) {
408
+ console.error('处理请求时发生错误:', error);
409
+ res.status(500).json({ error: "内部服务器错误,请查询日志记录!", details: error.message });
410
+ }
 
 
411
  });
412
 
413
  // 404处理
414
  app.use((req, res) => {
415
+ res.status(404).json({ message: "服务创建成功运行中,请根据规则使用正确请求路径" });
416
  });
417
 
418
  // 启动服务器
419
  app.listen(CONFIG.SERVER.PORT, () => {
420
+ console.log(`服务器运行在 http://localhost:${CONFIG.SERVER.PORT}`);
421
  });