File size: 4,109 Bytes
33668ef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import Koa from "koa";
import Router from "@koa/router";
import compose from "koa-compose";
import { koaBody } from "koa-body";
import {
  handleTimeStap,
  handleBase64,
  handleNewSession,
  handleErrors,
  initParams,
} from "./handles.js";
import https from "https";
import { createParser } from "eventsource-parser";
const app = new Koa();
const router = new Router();
const params = {};
const break_message = {
  answer_message_id: null,
  query_message_id: null,
  scene: params.scene,
  broken_pos: 0,
  conversation_id: params.conversation_id,
  local_message_id: params.local_message_id,
};

const createNewSession = async (ctx, next) => {
  const { messages } = ctx.request.body;
  const sessionid = ctx.req.headers.authorization.split(" ")[1];
  if (
    messages[0].role === "system" &&
    messages.length === 2 &&
    break_message.answer_message_id !== null &&
    break_message.query_message_id !== null
  ) {
    console.log("new session");
    await handleNewSession(break_message, sessionid);
    params.conversation_id =
      "7337495393033765890_7337496580645339154_2_" + handleTimeStap();
    params.local_message_id = handleBase64();
  }
  await next();
};

// 事件源路由
router.post("/api/v1/chat/completions", async (ctx) => {
  ctx.respond = false;
  ctx.type = "text/event-stream";
  ctx.status = 200;
  // 初始化响应头 sse
  ctx.set({
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
    "Content-Encoding": "none",
  });
  // 获取 post 请求的 body
  const { messages, model } = ctx.request.body;
  const sessionid = ctx.req.headers.authorization.split(" ")[1];
  // 返回的数据
  const sseData = {
    id: null,
    object: "chat.completion.chunk",
    created: handleTimeStap(),
    model: model,
    system_fingerprint: null,
    choices: [
      {
        index: 0,
        delta: { content: null },
        logprobs: null,
        finish_reason: null,
      },
    ],
  };
  // 远程服务器配置
  const options = {
    hostname: "www.coze.com",
    path: "/api/conversation/chat",
    port: 443,
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Cookie: `sessionid=${sessionid}`,
      "User-Agent":
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
    },
  };
  params.query = messages[messages.length - 1].content;
  const sse = https.request(options);

  sse.on("response", async (res) => {
    const parser = createParser((event) => {
      if (event.event === "message") {
        const data = JSON.parse(event.data);
        if (data.message.type === "answer") {
          sseData.id = data.conversation_id;
          sseData.choices[0].delta.content = data.message.content;
          if (data.is_finish) {
            sseData.choices[0].finish_reason = "stop";
          } else {
            sseData.choices[0].finish_reason = null;
          }
          ctx.res.write(`data:${JSON.stringify(sseData)}\n\n`);
          if (data.is_finish) {
            break_message.answer_message_id = data.message.reply_id;
            break_message.query_message_id = data.message.message_id;
            break_message.broken_pos = parseInt(
              data.message.extra_info.output_tokens
            );
            ctx.res.end();
          }
        }
      }
    });
    res.setEncoding("utf8");
    res.on("data", (d) => {
      parser.feed(d);
    });
    res.on("end", () => {
      console.log("response end", new Date().toLocaleString());
    });
  });
  sse.on("error", async (e) => {
    console.error(`problem with request: ${e.message}`);
  });
  sse.on("close", async () => {
    console.log("sse closed", new Date().toLocaleString());
  });

  sse.write(JSON.stringify(params));
  sse.end();
  ctx.respond = false;
});

// 组合中间件
const all = compose([
  handleErrors,
  koaBody(),
  createNewSession,
  router.routes(),
  router.allowedMethods(),
]);

// 注册中间件
app.use(all);

// 启动Koa应用
app.listen(3000, () => {
  console.log("Server started on port 3000");
  initParams(params);
});