Rfym21 commited on
Commit
657c09e
·
verified ·
1 Parent(s): b030890

Upload 6 files

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