dan92 commited on
Commit
b385d50
·
verified ·
1 Parent(s): ce8ed19

Upload 2 files

Browse files
Files changed (1) hide show
  1. src/index.js +156 -1
src/index.js CHANGED
@@ -215,8 +215,151 @@ router.post('/v1/chat/completions', async (ctx) => {
215
  }
216
  })
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
- // 获取models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  router.get('/v1/models', async (ctx) => {
221
  ctx.body = {
222
  object: "list",
@@ -229,6 +372,18 @@ router.get('/v1/models', async (ctx) => {
229
  }
230
  })
231
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
  // 注册路由
234
  app.use(router.routes()).use(router.allowedMethods())
 
215
  }
216
  })
217
 
218
+ // hf/v1 路由(复制 v1 路由的处理逻辑)
219
+ router.post('/hf/v1/chat/completions', async (ctx) => {
220
+ const { messages, stream = false, model = 'claude-3-5-sonnet' } = ctx.request.body
221
+ const session_id = ctx.get('Authorization')?.replace('Bearer ', '')
222
+
223
+ if (!session_id) {
224
+ ctx.status = 401
225
+ ctx.body = { error: '未提供有效的 session_id' }
226
+ return
227
+ }
228
+
229
+ try {
230
+ const response = await makeRequest(session_id, model, messages)
231
+ if (stream == "true" || stream == true) {
232
+ ctx.set({
233
+ 'Content-Type': 'text/event-stream',
234
+ 'Cache-Control': 'no-cache',
235
+ 'Connection': 'keep-alive',
236
+ })
237
+ } else {
238
+ ctx.set({
239
+ 'Content-Type': 'application/json',
240
+ })
241
+ }
242
+
243
+ const messageId = crypto.randomUUID()
244
+ const reader = response.body.getReader()
245
+ if (stream == "true" || stream == true) {
246
+ ctx.res.write(`data: ${JSON.stringify({
247
+ "id": `chatcmpl-${messageId}`,
248
+ "choices": [
249
+ {
250
+ "index": 0,
251
+ "delta": {
252
+ "content": "",
253
+ "role": "assistant"
254
+ }
255
+ }
256
+ ],
257
+ "created": Math.floor(Date.now() / 1000),
258
+ "model": models[`${model}`],
259
+ "object": "chat.completion.chunk"
260
+ })}\n\n`)
261
+ }
262
+
263
+ try {
264
+ let resBody = {}
265
+
266
+ while (true) {
267
+ const { done, value } = await reader.read()
268
+ if (done) {
269
+ if (stream == "true" || stream == true) {
270
+ // 发送完成标记
271
+ ctx.res.write('data: [DONE]\n\n')
272
+ }
273
+ break
274
+ }
275
+
276
+ if (stream) {
277
+ const text = new TextDecoder().decode(value)
278
+ const textContent = [...text.matchAll(/data:.*"}/g)]
279
+
280
+ textContent.forEach(item => {
281
+ if (!item[0]) {
282
+ return
283
+ }
284
 
285
+ const content = JSON.parse(item[0].replace("data: ", ''))
286
+ if (!content || !content.delta) {
287
+ return
288
+ }
289
+
290
+ // 发送增量内容
291
+ ctx.res.write(`data: ${JSON.stringify({
292
+ "id": `chatcmpl-${messageId}`,
293
+ "choices": [
294
+ {
295
+ "index": 0,
296
+ "delta": {
297
+ "content": content.delta
298
+ }
299
+ }
300
+ ],
301
+ "created": Math.floor(Date.now() / 1000),
302
+ "model": models[`${model}`],
303
+ "object": "chat.completion.chunk"
304
+ })}\n\n`)
305
+ })
306
+ } else {
307
+ const text = new TextDecoder().decode(value)
308
+ const textContent = [...text.matchAll(/data:.*"}/g)]
309
+
310
+ textContent.forEach(item => {
311
+ if (!item[0]) {
312
+ return
313
+ }
314
+
315
+ const content = JSON.parse(item[0].replace("data: ", ''))
316
+ if (!content || !content.field_value || content.field_name == 'session_state.answer_is_finished' || content.field_name == 'content' || content.field_name == 'session_state' || content.delta || content.type == 'project_field') {
317
+ return
318
+ }
319
+
320
+ resBody = {
321
+ id: `chatcmpl-${messageId}`,
322
+ object: 'chat.completion',
323
+ created: Math.floor(Date.now() / 1000),
324
+ model,
325
+ choices: [
326
+ {
327
+ index: 0,
328
+ message: {
329
+ role: 'assistant',
330
+ content: content.field_value,
331
+ },
332
+ finish_reason: 'stop',
333
+ },
334
+ ],
335
+ usage: {
336
+ prompt_tokens: 0,
337
+ completion_tokens: 0,
338
+ total_tokens: content.field_value.length,
339
+ },
340
+ }
341
+ })
342
+ }
343
+ }
344
+
345
+ if (stream == "false" || stream == false) {
346
+ ctx.body = resBody
347
+ } else {
348
+ ctx.res.end()
349
+ }
350
+ return
351
+ } catch (error) {
352
+ console.error('流式响应出错:', error)
353
+ ctx.res.end()
354
+ }
355
+ } catch (error) {
356
+ console.error('请求处理出错:', error)
357
+ ctx.status = 500
358
+ ctx.body = { error: '请求处理失败' }
359
+ }
360
+ })
361
+
362
+ // 获取models的v1路由
363
  router.get('/v1/models', async (ctx) => {
364
  ctx.body = {
365
  object: "list",
 
372
  }
373
  })
374
 
375
+ // 获取models的hf/v1路由
376
+ router.get('/hf/v1/models', async (ctx) => {
377
+ ctx.body = {
378
+ object: "list",
379
+ data: Object.keys(models).map(model => ({
380
+ id: model,
381
+ object: "model",
382
+ created: 1706745938,
383
+ owned_by: "genspark"
384
+ }))
385
+ }
386
+ })
387
 
388
  // 注册路由
389
  app.use(router.routes()).use(router.allowedMethods())