hins111 commited on
Commit
258d093
·
verified ·
1 Parent(s): 3bfe0f0

Create main.ts

Browse files
Files changed (1) hide show
  1. main.ts +171 -0
main.ts ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // deno run --allow-net --allow-env adapter.ts
2
+
3
+ import { serve } from "https://deno.land/std@0.203.0/http/server.ts";
4
+
5
+ // --- Hardcoded API Keys ---
6
+ const CLIENT_API_KEYS = new Set([
7
+ "sk-client-1234567890abcdef",
8
+ // add more client keys here
9
+ ]);
10
+
11
+ const CODEGEEX_TOKENS: {
12
+ token: string;
13
+ isValid: boolean;
14
+ lastUsed: number;
15
+ errorCount: number;
16
+ }[] = [
17
+ { token: "cgx-token-AAAA", isValid: true, lastUsed: 0, errorCount: 0 },
18
+ { token: "cgx-token-BBBB", isValid: true, lastUsed: 0, errorCount: 0 },
19
+ // add more CodeGeeX tokens here
20
+ ];
21
+
22
+ const MAX_ERROR_COUNT = 3;
23
+ const ERROR_COOLDOWN = 300 * 1000; // ms
24
+
25
+ // --- Utilities ---
26
+ function now(): number {
27
+ return Date.now();
28
+ }
29
+
30
+ function rotateToken(): typeof CODEGEEX_TOKENS[0] | null {
31
+ const available = CODEGEEX_TOKENS.filter(t => {
32
+ if (!t.isValid) return false;
33
+ if (t.errorCount >= MAX_ERROR_COUNT && now() - t.lastUsed < ERROR_COOLDOWN) return false;
34
+ return true;
35
+ });
36
+ if (available.length === 0) return null;
37
+
38
+ // reset cooled-down tokens
39
+ for (const t of available) {
40
+ if (t.errorCount >= MAX_ERROR_COUNT && now() - t.lastUsed >= ERROR_COOLDOWN) {
41
+ t.errorCount = 0;
42
+ }
43
+ }
44
+
45
+ // pick the one least recently used, then lowest errorCount
46
+ available.sort((a, b) => a.lastUsed - b.lastUsed || a.errorCount - b.errorCount);
47
+ const tok = available[0];
48
+ tok.lastUsed = now();
49
+ return tok;
50
+ }
51
+
52
+ async function proxyChat(req: Request, params: { stream: boolean; model: string; messages: any[] }) {
53
+ const tokenObj = rotateToken();
54
+ if (!tokenObj) {
55
+ return new Response(JSON.stringify({ error: "No valid CodeGeeX tokens available" }), { status: 503 });
56
+ }
57
+
58
+ const payload = {
59
+ user_role: 0,
60
+ ide: "Deno",
61
+ prompt: params.messages.slice(-1)[0]?.content || "",
62
+ history: [], // Simplified: not preserving history in this example
63
+ model: params.model,
64
+ };
65
+
66
+ try {
67
+ const response = await fetch("https://codegeex.cn/prod/code/chatCodeSseV3/chat", {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ "Accept": "text/event-stream",
72
+ "code-token": tokenObj.token,
73
+ },
74
+ body: JSON.stringify(payload),
75
+ });
76
+ if (!response.ok) {
77
+ if (response.status === 401 || response.status === 403) {
78
+ tokenObj.isValid = false;
79
+ } else {
80
+ tokenObj.errorCount++;
81
+ }
82
+ return new Response(JSON.stringify({ error: `Upstream error ${response.status}` }), { status: 502 });
83
+ }
84
+
85
+ if (params.stream) {
86
+ // proxy SSE
87
+ return new Response(response.body, {
88
+ status: 200,
89
+ headers: {
90
+ "Content-Type": "text/event-stream",
91
+ "Cache-Control": "no-cache",
92
+ Connection: "keep-alive",
93
+ },
94
+ });
95
+ } else {
96
+ // accumulate and return JSON
97
+ const text = await response.text();
98
+ return new Response(JSON.stringify({
99
+ id: `chatcmpl-${crypto.randomUUID()}`,
100
+ object: "chat.completion",
101
+ created: Math.floor(now() / 1000),
102
+ model: params.model,
103
+ choices: [{ message: { role: "assistant", content: text }, index: 0, finish_reason: "stop" }],
104
+ }), {
105
+ status: 200,
106
+ headers: { "Content-Type": "application/json" },
107
+ });
108
+ }
109
+ } catch (err) {
110
+ tokenObj.errorCount++;
111
+ return new Response(JSON.stringify({ error: err.message }), { status: 500 });
112
+ }
113
+ }
114
+
115
+ // --- Handlers ---
116
+ async function handler(req: Request): Promise<Response> {
117
+ const url = new URL(req.url);
118
+
119
+ // /debug?enable=true|false
120
+ if (url.pathname === "/debug" && req.method === "GET") {
121
+ const en = url.searchParams.get("enable");
122
+ if (en !== null) {
123
+ Deno.env.set("DEBUG_MODE", en);
124
+ }
125
+ return new Response(JSON.stringify({ debug_mode: Deno.env.get("DEBUG_MODE") === "true" }), {
126
+ headers: { "Content-Type": "application/json" },
127
+ });
128
+ }
129
+
130
+ // Authentication middleware
131
+ const auth = req.headers.get("Authorization")?.replace(/^Bearer\s+/, "");
132
+ if (!CLIENT_API_KEYS.size) {
133
+ return new Response(JSON.stringify({ error: "Server misconfigured: no client keys" }), { status: 503 });
134
+ }
135
+ if (!auth || !CLIENT_API_KEYS.has(auth)) {
136
+ return new Response(JSON.stringify({ error: "Invalid or missing API key" }), {
137
+ status: 401,
138
+ headers: { "WWW-Authenticate": "Bearer" },
139
+ });
140
+ }
141
+
142
+ // GET /v1/models and GET /models
143
+ if ((url.pathname === "/v1/models" || url.pathname === "/models") && req.method === "GET") {
144
+ const data = CODEGEEX_TOKENS.map((_, i) => ({
145
+ id: [`claude-3-7-sonnet`, `claude-sonnet-4`][i % 2],
146
+ owned_by: "anthropic",
147
+ created: Math.floor(now() / 1000),
148
+ object: "model",
149
+ }));
150
+ return new Response(JSON.stringify({ object: "list", data }), {
151
+ headers: { "Content-Type": "application/json" },
152
+ });
153
+ }
154
+
155
+ // POST /v1/chat/completions
156
+ if (url.pathname === "/v1/chat/completions" && req.method === "POST") {
157
+ const body = await req.json();
158
+ const { model, messages, stream = true } = body;
159
+ if (!model || !Array.isArray(messages)) {
160
+ return new Response(JSON.stringify({ error: "Bad Request" }), { status: 400 });
161
+ }
162
+ return proxyChat(req, { model, messages, stream });
163
+ }
164
+
165
+ // Not found
166
+ return new Response(JSON.stringify({ error: "Not Found" }), { status: 404 });
167
+ }
168
+
169
+ // --- Start Server ---
170
+ console.log("Starting Deno CodeGeeX Adapter on http://0.0.0.0:8000");
171
+ serve(handler, { port: 8000 });