dan92 commited on
Commit
d8a9f4c
·
verified ·
1 Parent(s): a3568ac

Upload 7 files

Browse files
Files changed (7) hide show
  1. .gitattributes +35 -35
  2. .gitignore +1 -0
  3. Dockerfile +11 -0
  4. README.md +12 -11
  5. package.json +17 -0
  6. src/index.js +428 -0
  7. src/models.js +14 -0
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ node_modules
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY package*.json ./
6
+ RUN npm install
7
+
8
+ COPY . .
9
+
10
+ EXPOSE 3000
11
+ CMD ["npm", "run", "dev"]
README.md CHANGED
@@ -1,11 +1,12 @@
1
- ---
2
- title: Gen2
3
- emoji: 👁
4
- colorFrom: pink
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
+ ---
2
+ title: Gen
3
+ emoji: 🐢
4
+ colorFrom: pink
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ app_port: 3000
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
package.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "genspark2api",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "dev": "node src/index.js"
7
+ },
8
+ "keywords": [],
9
+ "author": "",
10
+ "license": "ISC",
11
+ "description": "",
12
+ "dependencies": {
13
+ "koa": "^2.15.3",
14
+ "koa-bodyparser": "^4.4.1",
15
+ "koa-router": "^13.0.1"
16
+ }
17
+ }
src/index.js ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const Koa = require('koa')
2
+ const Router = require('koa-router')
3
+ const bodyParser = require('koa-bodyparser')
4
+ const models = require("./models")
5
+ const crypto = require('crypto')
6
+
7
+
8
+ const app = new Koa()
9
+ const router = new Router()
10
+
11
+ // 使用 bodyParser 中间件
12
+ app.use(bodyParser())
13
+
14
+ // 配置 bodyParser
15
+ app.use(bodyParser({
16
+ enableTypes: ['json', 'form', 'text'],
17
+ jsonLimit: '30mb', // JSON 数据大小限制
18
+ formLimit: '30mb', // form 数据大小限制
19
+ textLimit: '30mb', // text 数据大小限制
20
+ }))
21
+
22
+ // 添加 CORS 中间件
23
+ app.use(async (ctx, next) => {
24
+ ctx.set('Access-Control-Allow-Origin', '*');
25
+ ctx.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
26
+ ctx.set('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
27
+ ctx.set('Access-Control-Max-Age', '3600');
28
+
29
+ // 处理 OPTIONS 请求
30
+ if (ctx.method === 'OPTIONS') {
31
+ ctx.status = 204;
32
+ return;
33
+ }
34
+
35
+ await next();
36
+ });
37
+
38
+ // 添加请求日志中间件
39
+ app.use(async (ctx, next) => {
40
+ const start = Date.now()
41
+ console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.url} - Request started`)
42
+ try {
43
+ await next()
44
+ } catch (err) {
45
+ throw err
46
+ } finally {
47
+ const ms = Date.now() - start
48
+ console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.url} - Response ${ctx.status} - ${ms}ms`)
49
+ }
50
+ })
51
+
52
+ // 错误处理中间件
53
+ app.use(async (ctx, next) => {
54
+ try {
55
+ await next()
56
+ // 只有当没有任何路由处理请求时才设置 404
57
+ if (ctx.status === 404 && !ctx.body) {
58
+ ctx.status = 404
59
+ ctx.body = {
60
+ error: 'Not Found',
61
+ message: `The requested path ${ctx.path} was not found`,
62
+ availableEndpoints: [
63
+ '/v1/chat/completions',
64
+ '/hf/v1/chat/completions',
65
+ '/v1/models',
66
+ '/hf/v1/models'
67
+ ]
68
+ }
69
+ }
70
+ } catch (err) {
71
+ console.error('Error details:', {
72
+ path: ctx.path,
73
+ method: ctx.method,
74
+ error: err.message,
75
+ stack: err.stack
76
+ })
77
+ ctx.status = err.status || 500
78
+ ctx.body = {
79
+ error: err.name || 'Internal Server Error',
80
+ message: err.message,
81
+ path: ctx.path
82
+ }
83
+ ctx.app.emit('error', err, ctx)
84
+ }
85
+ })
86
+
87
+ const makeRequest = async (session_id, requestModel, messages) => {
88
+ console.log('开始请求:', {
89
+ session_id: '***',
90
+ model: requestModel,
91
+ messages_length: messages?.length,
92
+ timestamp: new Date().toISOString()
93
+ })
94
+ try {
95
+ // 设置请求头
96
+ const myHeaders = new Headers()
97
+ myHeaders.append("Cookie", `session_id=${session_id}`)
98
+ myHeaders.append("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
99
+ myHeaders.append("Content-Type", "application/json")
100
+ myHeaders.append("Accept", "*/*")
101
+ myHeaders.append("Host", "www.genspark.ai")
102
+ myHeaders.append("Connection", "keep-alive")
103
+
104
+ // 设置请求体
105
+ var body = JSON.stringify({
106
+ "type": "COPILOT_MOA_CHAT",
107
+ "current_query_string": "type=COPILOT_MOA_CHAT",
108
+ "messages": messages,
109
+ "action_params": {},
110
+ "extra_data": {
111
+ "models": [
112
+ models[`${requestModel}`] || models["claude-3-5-sonnet-20241022"]
113
+ ],
114
+ "run_with_another_model": false,
115
+ "writingContent": null
116
+ }
117
+ })
118
+
119
+ const requestConfig = {
120
+ method: 'POST',
121
+ headers: myHeaders,
122
+ body: body,
123
+ redirect: 'follow'
124
+ };
125
+
126
+ const response = await fetch("https://www.genspark.ai/api/copilot/ask", requestConfig)
127
+
128
+ console.log('收到响应:', {
129
+ status: response.status,
130
+ ok: response.ok,
131
+ headers: Object.fromEntries(response.headers.entries()),
132
+ timestamp: new Date().toISOString()
133
+ })
134
+
135
+ if (!response.ok) {
136
+ throw new Error(`HTTP error! status: ${response.status}`)
137
+ }
138
+
139
+ return response
140
+ } catch (error) {
141
+ console.error('请求出错:', {
142
+ error: error.message,
143
+ cause: error.cause?.message,
144
+ stack: error.stack,
145
+ timestamp: new Date().toISOString()
146
+ })
147
+ throw error
148
+ }
149
+ }
150
+
151
+ // 创建处理函数
152
+ const handleChatCompletions = async (ctx) => {
153
+ const { messages, stream = false, model = 'claude-3-5-sonnet' } = ctx.request.body
154
+ const session_id = ctx.get('Authorization')?.replace('Bearer ', '')
155
+
156
+ console.log('收到请求:', {
157
+ path: ctx.path,
158
+ model,
159
+ stream,
160
+ messages_count: messages?.length
161
+ })
162
+
163
+ if (!session_id) {
164
+ ctx.status = 401
165
+ ctx.body = { error: '未提供有效的 session_id' }
166
+ return
167
+ }
168
+
169
+ try {
170
+ const response = await makeRequest(session_id, model, messages)
171
+
172
+ if (!response.body) {
173
+ throw new Error('Response body is null')
174
+ }
175
+
176
+ if (stream == "true" || stream == true) {
177
+ ctx.status = 200
178
+ ctx.set({
179
+ 'Content-Type': 'text/event-stream',
180
+ 'Cache-Control': 'no-cache',
181
+ 'Connection': 'keep-alive',
182
+ })
183
+ } else {
184
+ ctx.status = 200
185
+ ctx.set({
186
+ 'Content-Type': 'application/json',
187
+ })
188
+ }
189
+
190
+ const messageId = crypto.randomUUID()
191
+ const reader = response.body.getReader()
192
+ let resBody = {
193
+ id: `chatcmpl-${messageId}`,
194
+ object: 'chat.completion',
195
+ created: Math.floor(Date.now() / 1000),
196
+ model,
197
+ choices: [],
198
+ usage: {
199
+ prompt_tokens: 0,
200
+ completion_tokens: 0,
201
+ total_tokens: 0,
202
+ },
203
+ }
204
+
205
+ if (stream == "true" || stream == true) {
206
+ ctx.res.write(`data: ${JSON.stringify({
207
+ "id": `chatcmpl-${messageId}`,
208
+ "choices": [
209
+ {
210
+ "index": 0,
211
+ "delta": {
212
+ "content": "",
213
+ "role": "assistant"
214
+ }
215
+ }
216
+ ],
217
+ "created": Math.floor(Date.now() / 1000),
218
+ "model": model,
219
+ "object": "chat.completion.chunk"
220
+ })}\n\n`)
221
+ }
222
+
223
+ try {
224
+ while (true) {
225
+ const { done, value } = await reader.read()
226
+ if (done) {
227
+ console.log('响应流结束', {
228
+ messageId,
229
+ timestamp: new Date().toISOString()
230
+ })
231
+ if (stream == "true" || stream == true) {
232
+ ctx.res.write('data: [DONE]\n\n')
233
+ }
234
+ break
235
+ }
236
+
237
+ if (stream == "true" || stream == true) {
238
+ const text = new TextDecoder().decode(value)
239
+ console.log('收到流数据:', {
240
+ length: text.length,
241
+ timestamp: new Date().toISOString()
242
+ })
243
+ const textContent = [...text.matchAll(/data:.*"}/g)]
244
+
245
+ textContent.forEach((item, index) => {
246
+ if (!item[0]) {
247
+ console.log('跳过空数据块:', { index })
248
+ return
249
+ }
250
+ try {
251
+ const content = JSON.parse(item[0].replace("data: ", ''))
252
+ if (!content || !content.delta) {
253
+ console.log('无效数据块:', { index, content })
254
+ return
255
+ }
256
+
257
+ ctx.res.write(`data: ${JSON.stringify({
258
+ "id": `chatcmpl-${messageId}`,
259
+ "choices": [
260
+ {
261
+ "index": 0,
262
+ "delta": {
263
+ "content": content.delta
264
+ }
265
+ }
266
+ ],
267
+ "created": Math.floor(Date.now() / 1000),
268
+ "model": model,
269
+ "object": "chat.completion.chunk"
270
+ })}\n\n`)
271
+ } catch (e) {
272
+ console.error('解析流数据失败:', {
273
+ index,
274
+ data: item[0],
275
+ error: e.message
276
+ })
277
+ }
278
+ })
279
+ } else {
280
+ const text = new TextDecoder().decode(value)
281
+ console.log('收到非流数据:', {
282
+ length: text.length,
283
+ timestamp: new Date().toISOString()
284
+ })
285
+ const textContent = [...text.matchAll(/data:.*"}/g)]
286
+
287
+ textContent.forEach((item, index) => {
288
+ if (!item[0]) {
289
+ console.log('跳过空数据块:', { index })
290
+ return
291
+ }
292
+ try {
293
+ const content = JSON.parse(item[0].replace("data: ", ''))
294
+ 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') {
295
+ console.log('跳过无效数据:', {
296
+ field_name: content?.field_name,
297
+ type: content?.type
298
+ })
299
+ return
300
+ }
301
+
302
+ resBody.choices.push({
303
+ index: 0,
304
+ message: {
305
+ role: 'assistant',
306
+ content: content.field_value,
307
+ },
308
+ finish_reason: 'stop',
309
+ })
310
+ resBody.usage.total_tokens = content.field_value.length
311
+ } catch (e) {
312
+ console.error('解析非流数据失败:', {
313
+ index,
314
+ data: item[0],
315
+ error: e.message
316
+ })
317
+ }
318
+ })
319
+ }
320
+ }
321
+
322
+ if (stream == "false" || stream == false) {
323
+ if (resBody.choices.length === 0) {
324
+ resBody.choices.push({
325
+ index: 0,
326
+ message: {
327
+ role: 'assistant',
328
+ content: '抱歉,我无法处理您的请求。',
329
+ },
330
+ finish_reason: 'stop',
331
+ })
332
+ }
333
+ ctx.body = resBody
334
+ } else {
335
+ ctx.res.end()
336
+ }
337
+ } catch (error) {
338
+ console.error('响应处理出错:', {
339
+ error: error.message,
340
+ cause: error.cause?.message,
341
+ stack: error.stack,
342
+ timestamp: new Date().toISOString()
343
+ })
344
+ if (stream == "true" || stream == true) {
345
+ try {
346
+ ctx.res.write(`data: ${JSON.stringify({
347
+ "id": `chatcmpl-${messageId}`,
348
+ "choices": [
349
+ {
350
+ "index": 0,
351
+ "delta": {
352
+ "content": "\n\n抱歉,处理您的请求时出现错误。"
353
+ }
354
+ }
355
+ ],
356
+ "created": Math.floor(Date.now() / 1000),
357
+ "model": model,
358
+ "object": "chat.completion.chunk"
359
+ })}\n\n`)
360
+ ctx.res.write('data: [DONE]\n\n')
361
+ } catch (e) {
362
+ console.error('发送错误信息失败:', e)
363
+ } finally {
364
+ ctx.res.end()
365
+ }
366
+ } else {
367
+ ctx.status = 500
368
+ ctx.body = { error: '响应处理失败', details: error.toString() }
369
+ }
370
+ }
371
+ } catch (error) {
372
+ console.error('请求处理出错:', error)
373
+ ctx.status = error.status || 500
374
+ ctx.body = {
375
+ error: error.message || '请求处理失败',
376
+ details: error.toString()
377
+ }
378
+ }
379
+ }
380
+
381
+ // 创建获取模型列表处理函数
382
+ const handleModels = async (ctx) => {
383
+ ctx.body = {
384
+ object: "list",
385
+ data: Object.keys(models).map(model => ({
386
+ id: model,
387
+ object: "model",
388
+ created: 1706745938,
389
+ owned_by: "genspark"
390
+ }))
391
+ }
392
+ }
393
+
394
+ // 注册路由 - 同时支持 /v1 和 /hf/v1 路径
395
+ router.post('/v1/chat/completions', handleChatCompletions)
396
+ router.post('/hf/v1/chat/completions', handleChatCompletions)
397
+ router.get('/v1/models', handleModels)
398
+ router.get('/hf/v1/models', handleModels)
399
+
400
+ // 添加根路由处理器
401
+ router.get('/', async (ctx) => {
402
+ ctx.body = {
403
+ status: 'ok',
404
+ message: 'API server is running',
405
+ version: '1.0.0',
406
+ endpoints: [
407
+ '/v1/chat/completions',
408
+ '/hf/v1/chat/completions',
409
+ '/v1/models',
410
+ '/hf/v1/models'
411
+ ]
412
+ }
413
+ })
414
+
415
+ // 注册路由中间件
416
+ app.use(router.routes()).use(router.allowedMethods())
417
+
418
+ // 启动服务器
419
+ const PORT = process.env.PORT || 3000
420
+ app.listen(PORT, '0.0.0.0', () => {
421
+ console.log('='.repeat(50))
422
+ console.log(`服务器启动于 http://0.0.0.0:${PORT}`)
423
+ console.log('环境变量:', {
424
+ PORT: process.env.PORT,
425
+ NODE_ENV: process.env.NODE_ENV
426
+ })
427
+ console.log('='.repeat(50))
428
+ });
src/models.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const models = {
2
+ "o1-preview": "o1-preview",
3
+ "o1-mini": "o1-preview",
4
+ "claude-3-5-sonnet-20240620": "claude-3-5-sonnet",
5
+ "claude-3-5-sonnet-20241022": "claude-3-5-sonnet",
6
+ "claude-3-5-haiku-20241022": "claude-3-5-haiku",
7
+ "claude-3-haiku-20240307": "claude-3-5-haiku",
8
+ "gpt-4o": "gpt-4o",
9
+ "gpt-4o-mini": "gpt-4o-mini",
10
+ "gemini-1.5-pro-latest": "gemini-1.5-pro",
11
+ "gemini-1.5-flash-latest": "gemini-1.5-flash"
12
+ }
13
+
14
+ module.exports = models