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
async function getResponse(req: Query, send: Sender) {
send("meta", { content_type: "text/markdown" });
const lastMessage = req.query.at(-1);
send("text", { text: `echo ${lastMessage.content}` });
send("done", {});
}
// IGNORE EVERYTHING BELOW THIS LINE //
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// protocol https://creator.poe.com/docs/poe-protocol-specification
type Query = {
version: string;
type: "query";
query: {
role: "system" | "user" | "bot";
content: string;
content_type: string;
timestamp: number;
message_id: string;
feedback: { type: "like" | "dislike"; reason?: string }[];
attachments: {
url: string;
content_type: string;
name: string;
parsed_content?: string;
}[];
}[];
message_id: string;
user_id: string;
conversation_id: string;
metadata: string;
language_code: string;
};
type Events = {
meta: { content_type: string };
text: { text: string };
replace_response: { text: string };
suggested_reply: { text: string };
error: { allow_retry?: boolean; text: string };
done: {};
};
type Sender<EventName extends keyof Events = keyof Events> = (eventName: EventName, data: Events[EventName]) => void;
function encodeEvent(event: string, data: any = {}) {
return new TextEncoder().encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
}
export default async function(req: Request): Promise<Response> {
const body = new ReadableStream({
start(controller) {
req.json().catch((e) => {
console.error("No json body");
return null;
}).then((reqBody) => {
const send = (event, data) => {
controller.enqueue(encodeEvent(event, data));
};
return getResponse(reqBody, send);
}).finally(() => {
controller.close();
});
},
});
return new Response(body, {
status: 200,
headers: {
"Content-Type": "text/event-stream",
},
});
}