smgc commited on
Commit
e09949e
·
verified ·
1 Parent(s): f7e6819

Update api/index.js

Browse files
Files changed (1) hide show
  1. api/index.js +347 -338
api/index.js CHANGED
@@ -1,100 +1,106 @@
1
- import grpc from '@huayue/grpc-js';
2
- import protoLoader from '@grpc/proto-loader';
3
- import {AutoRouter, cors, error, json} from 'itty-router';
4
- import dotenv from 'dotenv';
5
- import path,{ dirname } from 'path';
6
- import { fileURLToPath } from 'url';
7
- import {createServerAdapter} from '@whatwg-node/server';
8
- import {createServer} from 'http';
9
 
10
  // 加载环境变量
11
  dotenv.config();
 
12
  // 获取当前文件的目录路径(ESM 方式)
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
- // 初始化配置
15
  // 初始化配置
16
  class Config {
17
- constructor() {
18
- this.API_PREFIX = process.env.API_PREFIX || '/';
19
- this.API_KEY = process.env.API_KEY || '';
20
- this.MAX_RETRY_COUNT = process.env.MAX_RETRY_COUNT || 3;
21
- this.RETRY_DELAY = process.env.RETRY_DELAY || 5000;
22
- this.COMMON_GRPC = 'runtime-native-io-vertex-inference-grpc-service-lmuw6mcn3q-ul.a.run.app';
23
- this.COMMON_PROTO = path.join(__dirname,'..', 'protos', 'VertexInferenceService.proto')
24
- this.GPT_GRPC = 'runtime-native-io-gpt-inference-grpc-service-lmuw6mcn3q-ul.a.run.app';
25
- this.GPT_PROTO = path.join(__dirname,'..', 'protos', 'GPTInferenceService.proto')
26
- this.PORT = process.env.PORT || 8787;
27
- // 添加支持的模型列表
28
- this.SUPPORTED_MODELS = process.env.SUPPORTED_MODELS || [
29
- "gpt-4o-mini",
30
- "gpt-4o",
31
- "gpt-4-turbo",
32
- "gpt-4",
33
- "gpt-3.5-turbo",
34
- "claude-3-sonnet@20240229",
35
- "claude-3-opus@20240229",
36
- "claude-3-haiku@20240307",
37
- "claude-3-5-sonnet@20240620",
38
- "gemini-1.5-flash",
39
- "gemini-1.5-pro",
40
- "chat-bison",
41
- "codechat-bison"
42
- ];
43
- }
44
 
45
- // 添加模型验证方法
46
- isValidModel(model) {
47
- // 处理 Claude 模型的特殊格式
48
- const RegexInput = /^(claude-3-(5-sonnet|haiku|sonnet|opus))-(\d{8})$/;
49
- const matchInput = model.match(RegexInput);
50
- const normalizedModel = matchInput ? `${matchInput[1]}@${matchInput[3]}` : model;
51
 
52
- return this.SUPPORTED_MODELS.includes(normalizedModel);
53
- }
54
  }
 
55
  class GRPCHandler {
56
- constructor(protoFilePath) {
57
- // 动态加载传入的 .proto 文件路径
58
- this.packageDefinition = protoLoader.loadSync(protoFilePath, {
59
- keepCase: true,
60
- longs: String,
61
- enums: String,
62
- defaults: true,
63
- oneofs: true
64
- });
65
- }
66
  }
67
- const config = new Config();
 
68
  // 中间件
69
  // 添加运行回源
70
  const { preflight, corsify } = cors({
71
- origin: '*',
72
- allowMethods: '*',
73
- exposeHeaders: '*',
74
- });
75
 
76
  // 添加认证
77
  const withAuth = (request) => {
78
- if (config.API_KEY) {
79
- const authHeader = request.headers.get('Authorization');
80
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
81
- return error(401, 'Unauthorized: Missing or invalid Authorization header');
82
- }
83
- const token = authHeader.substring(7);
84
- if (token !== config.API_KEY) {
85
- return error(403, 'Forbidden: Invalid API key');
86
- }
87
- }
88
- };
 
89
  // 返回运行信息
90
  const logger = (res, req) => {
91
- console.log(req.method, res.status, req.url, Date.now() - req.start, 'ms');
92
- };
 
93
  const router = AutoRouter({
94
- before: [preflight], // 只保留 CORS preflight 检查
95
  missing: () => error(404, '404 not found.'),
96
  finally: [corsify, logger],
97
- });
 
98
  // Router路径
99
  router.get('/', () => json({
100
  service: "AI Chat Completion Proxy",
@@ -133,293 +139,296 @@ router.get('/', () => json({
133
  ],
134
  note: "Replace YOUR_API_KEY with your actual API key."
135
  }));
 
136
  router.get(config.API_PREFIX + '/v1/models', withAuth, () =>
137
- json({
138
- object: 'list',
139
- data: [
140
- { id: "gpt-4o-mini", object: "model", owned_by: "pieces-os" },
141
- { id: "gpt-4o", object: "model", owned_by: "pieces-os" },
142
- { id: "gpt-4-turbo", object: "model", owned_by: "pieces-os" },
143
- { id: "gpt-4", object: "model", owned_by: "pieces-os" },
144
- { id: "gpt-3.5-turbo", object: "model", owned_by: "pieces-os" },
145
- { id: "claude-3-sonnet-20240229", object: "model", owned_by: "pieces-os" },
146
- { id: "claude-3-opus-20240229", object: "model", owned_by: "pieces-os" },
147
- { id: "claude-3-haiku-20240307", object: "model", owned_by: "pieces-os" },
148
- { id: "claude-3-5-sonnet-20240620", object: "model", owned_by: "pieces-os" },
149
- { id: "gemini-1.5-flash", object: "model", owned_by: "pieces-os" },
150
- { id: "gemini-1.5-pro", object: "model", owned_by: "pieces-os" },
151
- { id: "chat-bison", object: "model", owned_by: "pieces-os" },
152
- { id: "codechat-bison", object: "model", owned_by: "pieces-os" },
153
- ],
154
- })
155
- );
 
156
  router.post(config.API_PREFIX + '/v1/chat/completions', withAuth, (req) => handleCompletion(req));
157
 
158
- async function GrpcToPieces(inputModel,OriginModel,message, rules, stream, temperature, top_p) {
159
- // 在非GPT类型的模型中,temperature和top_p是无效的
160
- // 使用系统的根证书
161
- const credentials = grpc.credentials.createSsl();
162
- let client,request;
163
- if (inputModel.includes('gpt')){
164
- // 加载proto文件
165
- const packageDefinition = new GRPCHandler(config.GPT_PROTO).packageDefinition;
166
- // 构建请求消息
167
- request = {
168
- models: inputModel,
169
- messages: [
170
- {role: 0, message: rules}, // system
171
- {role: 1, message: message} // user
172
- ],
173
- temperature:temperature || 0.1,
174
- top_p:top_p ?? 1,
175
- }
176
- // 获取gRPC对象
177
- const GRPCobjects = grpc.loadPackageDefinition(packageDefinition).runtime.aot.machine_learning.parents.gpt;
178
- client = new GRPCobjects.GPTInferenceService(config.GPT_GRPC, credentials);
179
- } else {
180
- // 加载proto文件
181
- const packageDefinition = new GRPCHandler(config.COMMON_PROTO).packageDefinition;
182
- // 构建请求消息
183
- request = {
184
- models: inputModel,
185
- args: {
186
- messages: {
187
- unknown: 1,
188
- message: message
189
- },
190
- rules: rules
191
- }
192
- };
193
- // 获取gRPC对象
194
- const GRPCobjects = grpc.loadPackageDefinition(packageDefinition).runtime.aot.machine_learning.parents.vertex;
195
- client = new GRPCobjects.VertexInferenceService(config.COMMON_GRPC, credentials);
196
- }
197
- return await ConvertOpenai(client,request,inputModel,OriginModel,stream);
198
  }
199
 
200
  async function messagesProcess(messages) {
201
- let rules = '';
202
- let message = '';
203
 
204
- for (const msg of messages) {
205
- let role = msg.role;
206
- // 格式化为字符串
207
- const contentStr = Array.isArray(msg.content)
208
- ? msg.content
209
- .filter((item) => item.text)
210
- .map((item) => item.text)
211
- .join('') || ''
212
- : msg.content;
213
- // 判断身份
214
- if (role === 'system') {
215
- rules += `system:${contentStr};\r\n`;
216
- } else if (['user', 'assistant'].includes(role)) {
217
- message += `${role}:${contentStr};\r\n`;
218
- }
219
- }
220
 
221
- return { rules, message };
222
  }
223
 
224
  async function ConvertOpenai(client, request, inputModel, OriginModel, stream) {
225
- const metadata = new grpc.Metadata();
226
- metadata.set('User-Agent', 'dart-grpc/2.0.0');
227
- for (let i = 0; i < config.MAX_RETRY_COUNT; i++) {
228
- try {
229
- if (stream) {
230
- const call = client.PredictWithStream(request,metadata);
231
- const encoder = new TextEncoder();
232
- const ReturnStream = new ReadableStream({
233
- start(controller) {
234
- // 处理数据
235
- call.on('data', (response) => {
236
- try {
237
- let response_code = Number(response.response_code);
238
- if (response_code === 204) {
239
- controller.close();
240
- call.destroy();
241
- } else if (response_code === 200) {
242
- let response_message;
243
- if (inputModel.includes('gpt')) {
244
- response_message = response.body.message_warpper.message.message;
245
- } else {
246
- response_message = response.args.args.args.message;
247
- }
248
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(ChatCompletionStreamWithModel(response_message, OriginModel))}\n\n`));
249
- } else {
250
- console.error(`Invalid response code: ${response_code}`);
251
- controller.error(error);
252
- }
253
- } catch (error) {
254
- console.error('Error processing stream data:', error);
255
- controller.error(error);
256
- }
257
- });
258
-
259
- // 处理错误
260
- call.on('error', (error) => {
261
- console.error('Stream error:', error);
262
- // 如果是 INTERNAL 错误且包含 RST_STREAM,可能是正常的流结束
263
- if (error.code === 13 && error.details.includes('RST_STREAM')) {
264
- controller.close();
265
- } else {
266
- controller.error(error);
267
- }
268
- call.destroy();
269
- });
270
-
271
- // 处理结束
272
- call.on('end', () => {
273
- controller.close();
274
- });
275
 
276
- // 处理取消
277
- return () => {
278
- call.destroy();
279
- };
280
- }
281
- });
 
 
 
 
 
282
 
283
- return new Response(ReturnStream, {
284
- headers: {
285
- 'Content-Type': 'text/event-stream',
286
- 'Connection': 'keep-alive',
287
- 'Cache-Control': 'no-cache',
288
- 'Transfer-Encoding': 'chunked'
289
- },
290
- });
291
- } else {
292
- // 非流式调用保持不变
293
- const call = await new Promise((resolve, reject) => {
294
- client.Predict(request,metadata, (err, response) => {
295
- if (err) reject(err);
296
- else resolve(response);
297
- });
298
- });
299
 
300
- let response_code = Number(call.response_code);
301
- if (response_code === 200) {
302
- let response_message;
303
- if (inputModel.includes('gpt')) {
304
- response_message = call.body.message_warpper.message.message;
305
- } else {
306
- response_message = call.args.args.args.message;
307
- }
308
- return new Response(JSON.stringify(ChatCompletionWithModel(response_message, OriginModel)), {
309
- headers: {
310
- 'Content-Type': 'application/json',
311
- },
312
- });
313
- }
314
- }
315
- } catch (err) {
316
- console.error(`Attempt ${i + 1} failed:`, err);
317
- if (i === config.MAX_RETRY_COUNT - 1) {
318
- return new Response(JSON.stringify({
319
- error: {
320
- message: "An error occurred while processing your request",
321
- type: "server_error",
322
- code: "internal_error",
323
- param: null
324
- }
325
- }), {
326
- status: 500,
327
- headers: {
328
- 'Content-Type': 'application/json'
329
- }
330
- });
331
- }
332
- await new Promise((resolve) => setTimeout(resolve, config.RETRY_DELAY));
333
- }
 
 
 
334
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  }
336
 
337
-
338
  function ChatCompletionWithModel(message, model) {
339
- return {
340
- id: 'Chat-Nekohy',
341
- object: 'chat.completion',
342
- created: Date.now(),
343
- model,
344
- usage: {
345
- prompt_tokens: 0,
346
- completion_tokens: 0,
347
- total_tokens: 0,
348
- },
349
- choices: [
350
- {
351
- message: {
352
- content: message,
353
- role: 'assistant',
354
- },
355
- index: 0,
356
- },
357
- ],
358
- };
359
  }
360
 
361
  function ChatCompletionStreamWithModel(text, model) {
362
- return {
363
- id: 'chatcmpl-Nekohy',
364
- object: 'chat.completion.chunk',
365
- created: 0,
366
- model,
367
- choices: [
368
- {
369
- index: 0,
370
- delta: {
371
- content: text,
372
- },
373
- finish_reason: null,
374
- },
375
- ],
376
- };
377
  }
378
 
379
  async function handleCompletion(request) {
380
- try {
381
- // todo stream逆向接口
382
- // 解析openai格式API请求
383
- const { model: OriginModel, messages, stream,temperature,top_p} = await request.json();
384
- const RegexInput = /^(claude-3-(5-sonnet|haiku|sonnet|opus))-(\d{8})$/;
385
- const matchInput = OriginModel.match(RegexInput);
386
- const inputModel = matchInput ? `${matchInput[1]}@${matchInput[3]}` : OriginModel;
387
- // 添加模型验证
388
- if (!config.isValidModel(inputModel)) {
389
- return new Response(
390
- JSON.stringify({
391
- error: {
392
- message: `Model '${OriginModel}' does not exist`,
393
- type: "invalid_request_error",
394
- param: "model",
395
- code: "model_not_found"
396
- }
397
- }),
398
- {
399
- status: 404,
400
- headers: {
401
- 'Content-Type': 'application/json'
402
- }
403
- }
404
- );
405
- }
406
- console.log(inputModel,messages,stream)
407
- // 解析system和user/assistant消息
408
- const { rules, message:content } = await messagesProcess(messages);
409
- console.log(rules,content)
410
- // 响应码,回复的消息
411
- return await GrpcToPieces(inputModel,OriginModel,content, rules, stream, temperature, top_p);
412
- } catch (err) {
413
- return error(500, err.message);
414
- }
415
  }
416
 
417
- (async () => {
418
- //For Cloudflare Workers
419
- if (typeof addEventListener === 'function') return;
420
- // For Nodejs
421
- const ittyServer = createServerAdapter(router.fetch);
422
- console.log(`Listening on http://localhost:${config.PORT}`);
423
- const httpServer = createServer(ittyServer);
424
- httpServer.listen(config.PORT);
425
- })();
 
1
+ import grpc from '@huayue/grpc-js'
2
+ import protoLoader from '@grpc/proto-loader'
3
+ import { AutoRouter, cors, error, json } from 'itty-router'
4
+ import dotenv from 'dotenv'
5
+ import path, { dirname } from 'path'
6
+ import { fileURLToPath } from 'url'
7
+ import { createServerAdapter } from '@whatwg-node/server'
8
+ import { createServer } from 'http'
9
 
10
  // 加载环境变量
11
  dotenv.config();
12
+
13
  // 获取当前文件的目录路径(ESM 方式)
14
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
+
16
  // 初始化配置
17
  class Config {
18
+ constructor() {
19
+ this.API_PREFIX = process.env.API_PREFIX || '/'
20
+ this.API_KEY = process.env.API_KEY || ''
21
+ this.MAX_RETRY_COUNT = process.env.MAX_RETRY_COUNT || 3
22
+ this.RETRY_DELAY = process.env.RETRY_DELAY || 5000
23
+ this.COMMON_GRPC = 'runtime-native-io-vertex-inference-grpc-service-lmuw6mcn3q-ul.a.run.app'
24
+ this.COMMON_PROTO = path.join(__dirname, '..', 'protos', 'VertexInferenceService.proto')
25
+ this.GPT_GRPC = 'runtime-native-io-gpt-inference-grpc-service-lmuw6mcn3q-ul.a.run.app'
26
+ this.GPT_PROTO = path.join(__dirname, '..', 'protos', 'GPTInferenceService.proto')
27
+ this.PORT = process.env.PORT || 8787
28
+ // 添加支持的模型列表
29
+ this.SUPPORTED_MODELS = process.env.SUPPORTED_MODELS || [
30
+ 'gpt-4o-mini',
31
+ 'gpt-4o',
32
+ 'gpt-4-turbo',
33
+ 'gpt-4',
34
+ 'gpt-3.5-turbo',
35
+ 'claude-3-sonnet@20240229',
36
+ 'claude-3-opus@20240229',
37
+ 'claude-3-haiku@20240307',
38
+ 'claude-3-5-sonnet@20240620',
39
+ 'gemini-1.5-flash',
40
+ 'gemini-1.5-pro',
41
+ 'chat-bison',
42
+ 'codechat-bison',
43
+ ]
44
+ }
45
 
46
+ // 添加模型验证方法
47
+ isValidModel(model) {
48
+ // 处理 Claude 模型的特殊格式
49
+ const RegexInput = /^(claude-3-(5-sonnet|haiku|sonnet|opus))-(\d{8})$/
50
+ const matchInput = model.match(RegexInput)
51
+ const normalizedModel = matchInput ? `${matchInput[1]}@${matchInput[3]}` : model
52
 
53
+ return this.SUPPORTED_MODELS.includes(normalizedModel)
54
+ }
55
  }
56
+
57
  class GRPCHandler {
58
+ constructor(protoFilePath) {
59
+ // 动态加载传入的 .proto 文件路径
60
+ this.packageDefinition = protoLoader.loadSync(protoFilePath, {
61
+ keepCase: true,
62
+ longs: String,
63
+ enums: String,
64
+ defaults: true,
65
+ oneofs: true,
66
+ })
67
+ }
68
  }
69
+ const config = new Config()
70
+
71
  // 中间件
72
  // 添加运行回源
73
  const { preflight, corsify } = cors({
74
+ origin: '*',
75
+ allowMethods: '*',
76
+ exposeHeaders: '*',
77
+ })
78
 
79
  // 添加认证
80
  const withAuth = (request) => {
81
+ if (config.API_KEY) {
82
+ const authHeader = request.headers.get('Authorization')
83
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
84
+ return error(401, 'Unauthorized: Missing or invalid Authorization header')
85
+ }
86
+ const token = authHeader.substring(7)
87
+ if (token !== config.API_KEY) {
88
+ return error(403, 'Forbidden: Invalid API key')
89
+ }
90
+ }
91
+ }
92
+
93
  // 返回运行信息
94
  const logger = (res, req) => {
95
+ console.log(req.method, res.status, req.url, Date.now() - req.start, 'ms')
96
+ }
97
+
98
  const router = AutoRouter({
99
+ before: [preflight],
100
  missing: () => error(404, '404 not found.'),
101
  finally: [corsify, logger],
102
+ })
103
+
104
  // Router路径
105
  router.get('/', () => json({
106
  service: "AI Chat Completion Proxy",
 
139
  ],
140
  note: "Replace YOUR_API_KEY with your actual API key."
141
  }));
142
+
143
  router.get(config.API_PREFIX + '/v1/models', withAuth, () =>
144
+ json({
145
+ object: 'list',
146
+ data: [
147
+ { id: 'gpt-4o-mini', object: 'model', owned_by: 'pieces-os' },
148
+ { id: 'gpt-4o', object: 'model', owned_by: 'pieces-os' },
149
+ { id: 'gpt-4-turbo', object: 'model', owned_by: 'pieces-os' },
150
+ { id: 'gpt-4', object: 'model', owned_by: 'pieces-os' },
151
+ { id: 'gpt-3.5-turbo', object: 'model', owned_by: 'pieces-os' },
152
+ { id: 'claude-3-sonnet-20240229', object: 'model', owned_by: 'pieces-os' },
153
+ { id: 'claude-3-opus-20240229', object: 'model', owned_by: 'pieces-os' },
154
+ { id: 'claude-3-haiku-20240307', object: 'model', owned_by: 'pieces-os' },
155
+ { id: 'claude-3-5-sonnet-20240620', object: 'model', owned_by: 'pieces-os' },
156
+ { id: 'gemini-1.5-flash', object: 'model', owned_by: 'pieces-os' },
157
+ { id: 'gemini-1.5-pro', object: 'model', owned_by: 'pieces-os' },
158
+ { id: 'chat-bison', object: 'model', owned_by: 'pieces-os' },
159
+ { id: 'codechat-bison', object: 'model', owned_by: 'pieces-os' },
160
+ ],
161
+ }),
162
+ )
163
+
164
  router.post(config.API_PREFIX + '/v1/chat/completions', withAuth, (req) => handleCompletion(req));
165
 
166
+ async function GrpcToPieces(inputModel, OriginModel, message, rules, stream, temperature, top_p) {
167
+ // 在非GPT类型的模型中,temperature和top_p是无效的
168
+ // 使用系统的根证书
169
+ const credentials = grpc.credentials.createSsl()
170
+ let client, request
171
+ if (inputModel.includes('gpt')) {
172
+ // 加载proto文件
173
+ const packageDefinition = new GRPCHandler(config.GPT_PROTO).packageDefinition
174
+ // 构建请求消息
175
+ request = {
176
+ models: inputModel,
177
+ messages: [
178
+ { role: 0, message: rules }, // system
179
+ { role: 1, message: message }, // user
180
+ ],
181
+ temperature: temperature || 0.1,
182
+ top_p: top_p ?? 1,
183
+ }
184
+ // 获取gRPC对象
185
+ const GRPCobjects = grpc.loadPackageDefinition(packageDefinition).runtime.aot.machine_learning.parents.gpt
186
+ client = new GRPCobjects.GPTInferenceService(config.GPT_GRPC, credentials)
187
+ } else {
188
+ // 加载proto文件
189
+ const packageDefinition = new GRPCHandler(config.COMMON_PROTO).packageDefinition
190
+ // 构建请求消息
191
+ request = {
192
+ models: inputModel,
193
+ args: {
194
+ messages: {
195
+ unknown: 1,
196
+ message: message,
197
+ },
198
+ rules: rules,
199
+ },
200
+ }
201
+ // 获取gRPC对象
202
+ const GRPCobjects = grpc.loadPackageDefinition(packageDefinition).runtime.aot.machine_learning.parents.vertex
203
+ client = new GRPCobjects.VertexInferenceService(config.COMMON_GRPC, credentials)
204
+ }
205
+ return await ConvertOpenai(client, request, inputModel, OriginModel, stream)
206
  }
207
 
208
  async function messagesProcess(messages) {
209
+ let rules = ''
210
+ let message = ''
211
 
212
+ for (const msg of messages) {
213
+ let role = msg.role
214
+ // 格式化为字符串
215
+ const contentStr = Array.isArray(msg.content)
216
+ ? msg.content
217
+ .filter((item) => item.text)
218
+ .map((item) => item.text)
219
+ .join('') || ''
220
+ : msg.content
221
+ // 判断身份
222
+ if (role === 'system') {
223
+ rules += `system:${contentStr};\r\n`
224
+ } else if (['user', 'assistant'].includes(role)) {
225
+ message += `${role}:${contentStr};\r\n`
226
+ }
227
+ }
228
 
229
+ return { rules, message }
230
  }
231
 
232
  async function ConvertOpenai(client, request, inputModel, OriginModel, stream) {
233
+ const metadata = new grpc.Metadata()
234
+ metadata.set('User-Agent', 'dart-grpc/2.0.0')
235
+ for (let i = 0; i < config.MAX_RETRY_COUNT; i++) {
236
+ try {
237
+ if (stream) {
238
+ const call = client.PredictWithStream(request, metadata)
239
+ const encoder = new TextEncoder()
240
+ const ReturnStream = new ReadableStream({
241
+ start(controller) {
242
+ // 处理数据
243
+ call.on('data', (response) => {
244
+ try {
245
+ let response_code = Number(response.response_code)
246
+ if (response_code === 204) {
247
+ controller.close()
248
+ call.destroy()
249
+ } else if (response_code === 200) {
250
+ let response_message
251
+ if (inputModel.includes('gpt')) {
252
+ response_message = response.body.message_warpper.message.message
253
+ } else {
254
+ response_message = response.args.args.args.message
255
+ }
256
+ controller.enqueue(
257
+ encoder.encode(`data: ${JSON.stringify(ChatCompletionStreamWithModel(response_message, OriginModel))}\n\n`),
258
+ )
259
+ } else {
260
+ console.error(`Invalid response code: ${response_code}`)
261
+ controller.error(error)
262
+ }
263
+ } catch (error) {
264
+ console.error('Error processing stream data:', error)
265
+ controller.error(error)
266
+ }
267
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
+ // 处理错误
270
+ call.on('error', (error) => {
271
+ console.error('Stream error:', error)
272
+ // 如果是 INTERNAL 错误且包含 RST_STREAM,可能是正常的流结束
273
+ if (error.code === 13 && error.details.includes('RST_STREAM')) {
274
+ controller.close()
275
+ } else {
276
+ controller.error(error)
277
+ }
278
+ call.destroy()
279
+ })
280
 
281
+ // 处理结束
282
+ call.on('end', () => {
283
+ controller.close()
284
+ })
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ // 处理取消
287
+ return () => {
288
+ call.destroy()
289
+ }
290
+ },
291
+ })
292
+ return new Response(ReturnStream, {
293
+ headers: {
294
+ 'Content-Type': 'text/event-stream',
295
+ Connection: 'keep-alive',
296
+ 'Cache-Control': 'no-cache',
297
+ 'Transfer-Encoding': 'chunked',
298
+ },
299
+ })
300
+ } else {
301
+ // 非流式调用保持不变
302
+ const call = await new Promise((resolve, reject) => {
303
+ client.Predict(request, metadata, (err, response) => {
304
+ if (err) reject(err)
305
+ else resolve(response)
306
+ })
307
+ })
308
+ let response_code = Number(call.response_code)
309
+ if (response_code === 200) {
310
+ let response_message
311
+ if (inputModel.includes('gpt')) {
312
+ response_message = call.body.message_warpper.message.message
313
+ } else {
314
+ response_message = call.args.args.args.message
315
+ }
316
+ return new Response(JSON.stringify(ChatCompletionWithModel(response_message, OriginModel)), {
317
+ headers: {
318
+ 'Content-Type': 'application/json',
319
+ },
320
+ })
321
+ } else {
322
+ throw new Error(`Invalid response code: ${response_code}`)
323
  }
324
+ }
325
+ } catch (err) {
326
+ console.error(`Attempt ${i + 1} failed:`, err)
327
+ await new Promise((resolve) => setTimeout(resolve, config.RETRY_DELAY))
328
+ }
329
+ }
330
+ return new Response(
331
+ JSON.stringify({
332
+ error: {
333
+ message: 'An error occurred while processing your request',
334
+ type: 'server_error',
335
+ code: 'internal_error',
336
+ param: null,
337
+ },
338
+ }),
339
+ {
340
+ status: 500,
341
+ headers: {
342
+ 'Content-Type': 'application/json',
343
+ },
344
+ },
345
+ )
346
  }
347
 
 
348
  function ChatCompletionWithModel(message, model) {
349
+ return {
350
+ id: 'Chat-Nekohy',
351
+ object: 'chat.completion',
352
+ created: Date.now(),
353
+ model,
354
+ usage: {
355
+ prompt_tokens: 0,
356
+ completion_tokens: 0,
357
+ total_tokens: 0,
358
+ },
359
+ choices: [
360
+ {
361
+ message: {
362
+ content: message,
363
+ role: 'assistant',
364
+ },
365
+ index: 0,
366
+ },
367
+ ],
368
+ }
369
  }
370
 
371
  function ChatCompletionStreamWithModel(text, model) {
372
+ return {
373
+ id: 'chatcmpl-Nekohy',
374
+ object: 'chat.completion.chunk',
375
+ created: 0,
376
+ model,
377
+ choices: [
378
+ {
379
+ index: 0,
380
+ delta: {
381
+ content: text,
382
+ },
383
+ finish_reason: null,
384
+ },
385
+ ],
386
+ }
387
  }
388
 
389
  async function handleCompletion(request) {
390
+ try {
391
+ // 解析openai格式API请求
392
+ const { model: OriginModel, messages, stream, temperature, top_p } = await request.json()
393
+ const RegexInput = /^(claude-3-(5-sonnet|haiku|sonnet|opus))-(\d{8})$/
394
+ const matchInput = OriginModel.match(RegexInput)
395
+ const inputModel = matchInput ? `${matchInput[1]}@${matchInput[3]}` : OriginModel
396
+ // 添加模型验证
397
+ if (!config.isValidModel(inputModel)) {
398
+ return new Response(
399
+ JSON.stringify({
400
+ error: {
401
+ message: `Model '${OriginModel}' does not exist`,
402
+ type: 'invalid_request_error',
403
+ param: 'model',
404
+ code: 'model_not_found',
405
+ },
406
+ }),
407
+ {
408
+ status: 404,
409
+ headers: {
410
+ 'Content-Type': 'application/json',
411
+ },
412
+ },
413
+ )
414
+ }
415
+ console.log(inputModel, messages, stream)
416
+ // 解析system和user/assistant消息
417
+ const { rules, message: content } = await messagesProcess(messages)
418
+ console.log(rules, content)
419
+ // 响应码,回复的消息
420
+ return await GrpcToPieces(inputModel, OriginModel, content, rules, stream, temperature, top_p)
421
+ } catch (err) {
422
+ return error(500, err.message)
423
+ }
 
424
  }
425
 
426
+ ;(async () => {
427
+ //For Cloudflare Workers
428
+ if (typeof addEventListener === 'function') return
429
+ // For Nodejs
430
+ const ittyServer = createServerAdapter(router.fetch)
431
+ console.log(`Listening on http://localhost:${config.PORT}`)
432
+ const httpServer = createServer(ittyServer)
433
+ httpServer.listen(config.PORT)
434
+ })()