BOHE commited on
Commit
b44e579
·
1 Parent(s): af4375a
Files changed (1) hide show
  1. api/main.go +179 -206
api/main.go CHANGED
@@ -24,7 +24,7 @@ type TokenCount struct {
24
  }
25
 
26
  const (
27
- MaxContextTokens = 4000 // 最大上下文 token 数
28
  )
29
 
30
  // YouChatResponse 定义了从 You.com API 接收的单个 token 的结构。
@@ -240,30 +240,80 @@ func Handler(w http.ResponseWriter, r *http.Request) {
240
  // 转换 system 消息为 user 消息
241
  openAIReq.Messages = convertSystemToUser(openAIReq.Messages)
242
 
243
- // 计算最后一条消息的 token 数(使用字符估算方法)
244
- lastMessage := openAIReq.Messages[len(openAIReq.Messages)-1]
245
- lastMessageTokens, err := countTokens([]Message{lastMessage})
246
- if err != nil {
247
- http.Error(w, "Failed to count tokens", http.StatusInternalServerError)
248
- return
249
- }
250
-
251
  // 构建 You.com 聊天历史
252
  var chatHistory []map[string]interface{}
253
- for _, msg := range openAIReq.Messages[:len(openAIReq.Messages)-1] { // 不包含最后一条消息
254
- chatMsg := map[string]interface{}{
255
- "question": msg.Content,
256
- "answer": "",
257
- }
258
- // 如果是 assistant 的消息, 则交换 question 和 answer
259
- if msg.Role == "assistant" {
260
- chatMsg["question"] = ""
261
- chatMsg["answer"] = msg.Content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
- chatHistory = append(chatHistory, chatMsg)
264
  }
265
 
266
- chatHistoryJSON, _ := json.Marshal(chatHistory) // 将聊天历史序列化为 JSON
 
 
 
 
 
 
 
 
267
 
268
  // 创建 You.com API 请求
269
  youReq, _ := http.NewRequest("GET", "https://you.com/api/streamingSearch", nil)
@@ -273,23 +323,48 @@ func Handler(w http.ResponseWriter, r *http.Request) {
273
  conversationTurnId := uuid.New().String()
274
  traceId := fmt.Sprintf("%s|%s|%s", chatId, conversationTurnId, time.Now().Format(time.RFC3339))
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  // 如果最后一条消息超过限制,使用文件上传
277
  if lastMessageTokens > MaxContextTokens {
278
- // 1. 获取 nonce
279
  nonceResp, err := getNonce(dsToken)
280
  if err != nil {
281
  fmt.Printf("获取 nonce 失败: %v\n", err)
282
  http.Error(w, "Failed to get nonce", http.StatusInternalServerError)
283
  return
284
  }
285
- fmt.Printf("获取到 nonce: %s\n", nonceResp.Uuid)
286
-
287
- // 4. 修改消息列表,保留历史记录,只将最后一条消息改为文件引用
288
- lastMsg := openAIReq.Messages[len(openAIReq.Messages)-1]
289
 
290
- // 创建临时文件,包含最后一条消息的内容
291
  tempFile := fmt.Sprintf("temp_%s.txt", nonceResp.Uuid)
292
- if err := os.WriteFile(tempFile, []byte(lastMsg.Content), 0644); err != nil {
293
  fmt.Printf("创建临时文件失败: %v\n", err)
294
  http.Error(w, "Failed to create temp file", http.StatusInternalServerError)
295
  return
@@ -305,197 +380,95 @@ func Handler(w http.ResponseWriter, r *http.Request) {
305
  }
306
 
307
  // 添加文件源信息
308
- sources := []map[string]interface{}{
309
- {
310
- "source_type": "user_file",
311
- "filename": uploadResp.Filename,
312
- "user_filename": uploadResp.UserFilename,
313
- "size_bytes": len(lastMsg.Content),
314
- },
315
- }
316
- sourcesJSON, err := json.Marshal(sources)
317
- if err != nil {
318
- fmt.Printf("序列化 sources 失败: %v\n", err)
319
- http.Error(w, "Failed to marshal sources", http.StatusInternalServerError)
320
- return
321
- }
322
-
323
- // 更新查询参数
324
- q := youReq.URL.Query()
325
-
326
- // 如果是第一次聊天(没有历史记录),使用空数组
327
- chatJSON := "[]"
328
- pastChatLength := "0"
329
- if len(chatHistory) > 0 {
330
- chatJSON = string(chatHistoryJSON)
331
- pastChatLength = fmt.Sprintf("%d", len(chatHistory))
332
- }
333
 
334
- // 按照官方URL格式设置参数
335
- q.Add("page", "1")
336
- q.Add("count", "10")
337
- q.Add("safeSearch", "Off")
338
- q.Add("mkt", "en-US")
339
- q.Add("enable_worklow_generation_ux", "true")
340
- q.Add("domain", "youchat")
341
- q.Add("use_personalization_extraction", "true")
342
- q.Add("queryTraceId", chatId)
343
- q.Add("chatId", chatId)
344
- q.Add("conversationTurnId", conversationTurnId)
345
- q.Add("pastChatLength", pastChatLength)
346
- q.Add("selectedChatMode", "custom")
347
  q.Add("sources", string(sourcesJSON))
348
- q.Add("selectedAiModel", mapModelName(openAIReq.Model))
349
- q.Add("enable_agent_clarification_questions", "true")
350
- q.Add("traceId", traceId)
351
- q.Add("use_nested_youchat_updates", "true")
352
- q.Add("q", fmt.Sprintf("Please review the attached file: %s", uploadResp.UserFilename))
353
- q.Add("chat", chatJSON)
354
 
355
- youReq.URL.RawQuery = q.Encode()
356
-
357
- fmt.Printf("\n=== 完整请求信息 ===\n")
358
- fmt.Printf("请求 URL: %s\n", youReq.URL.String())
359
- fmt.Printf("请求头:\n")
360
- for key, values := range youReq.Header {
361
- fmt.Printf("%s: %v\n", key, values)
362
- }
363
-
364
- // 设置请求头
365
- youReq.Header = http.Header{
366
- "sec-ch-ua-platform": {"Windows"},
367
- "Cache-Control": {"no-cache"},
368
- "sec-ch-ua": {`"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"`},
369
- "sec-ch-ua-bitness": {"64"},
370
- "sec-ch-ua-model": {""},
371
- "sec-ch-ua-mobile": {"?0"},
372
- "sec-ch-ua-arch": {"x86"},
373
- "sec-ch-ua-full-version": {"133.0.3065.39"},
374
- "Accept": {"text/event-stream"},
375
- "User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0"},
376
- "sec-ch-ua-platform-version": {"19.0.0"},
377
- "Sec-Fetch-Site": {"same-origin"},
378
- "Sec-Fetch-Mode": {"cors"},
379
- "Sec-Fetch-Dest": {"empty"},
380
- "Host": {"you.com"},
381
- }
382
-
383
- // 设置 Cookie
384
- cookies := getCookies(dsToken)
385
- var cookieStrings []string
386
- for name, value := range cookies {
387
- cookieStrings = append(cookieStrings, fmt.Sprintf("%s=%s", name, value))
388
- }
389
- youReq.Header.Add("Cookie", strings.Join(cookieStrings, ";"))
390
- fmt.Printf("Cookie: %s\n", strings.Join(cookieStrings, ";"))
391
- fmt.Printf("===================\n\n")
392
-
393
- // 发送请求并获取响应
394
- client := &http.Client{}
395
- resp, err := client.Do(youReq)
396
- if err != nil {
397
- fmt.Printf("发送请求失败: %v\n", err)
398
- http.Error(w, err.Error(), http.StatusInternalServerError)
399
- return
400
  }
401
- defer resp.Body.Close()
 
402
 
403
- // 打印响应状态码
404
- fmt.Printf("响应状态码: %d\n", resp.StatusCode)
405
 
406
- // 如果状态码不是 200,打印响应内容
407
- if resp.StatusCode != http.StatusOK {
408
- body, _ := io.ReadAll(resp.Body)
409
- fmt.Printf("错误响应内容: %s\n", string(body))
410
- http.Error(w, fmt.Sprintf("API returned status %d", resp.StatusCode), resp.StatusCode)
411
- return
412
- }
413
-
414
- // 根据 OpenAI 请求的 stream 参数选择处理函数
415
- if !openAIReq.Stream {
416
- handleNonStreamingResponse(w, youReq) // 处理非流式响应
417
- return
418
- }
419
 
420
- handleStreamingResponse(w, youReq) // 处理流式响应
421
- } else {
422
- // 构建常规查询参数
423
- q := youReq.URL.Query()
424
-
425
- // 如果是第一次聊天(没有历史记录),使用空数组
426
- chatJSON := "[]"
427
- pastChatLength := "0"
428
- if len(chatHistory) > 0 {
429
- chatJSON = string(chatHistoryJSON)
430
- pastChatLength = fmt.Sprintf("%d", len(chatHistory))
431
- }
 
 
 
 
 
 
432
 
433
- // 按照官方URL格式设置参数
434
- q.Add("page", "1")
435
- q.Add("count", "10")
436
- q.Add("safeSearch", "Off")
437
- q.Add("mkt", "en-US")
438
- q.Add("enable_worklow_generation_ux", "true")
439
- q.Add("domain", "youchat")
440
- q.Add("use_personalization_extraction", "true")
441
- q.Add("queryTraceId", chatId)
442
- q.Add("chatId", chatId)
443
- q.Add("conversationTurnId", conversationTurnId)
444
- q.Add("pastChatLength", pastChatLength)
445
- q.Add("selectedChatMode", "custom")
446
- q.Add("selectedAiModel", mapModelName(openAIReq.Model))
447
- q.Add("enable_agent_clarification_questions", "true")
448
- q.Add("traceId", traceId)
449
- q.Add("use_nested_youchat_updates", "true")
450
- q.Add("q", openAIReq.Messages[len(openAIReq.Messages)-1].Content)
451
- q.Add("chat", chatJSON)
452
-
453
- youReq.URL.RawQuery = q.Encode()
454
-
455
- fmt.Printf("\n=== 完整请求信息 ===\n")
456
- fmt.Printf("请求 URL: %s\n", youReq.URL.String())
457
- fmt.Printf("请求头:\n")
458
- for key, values := range youReq.Header {
459
- fmt.Printf("%s: %v\n", key, values)
460
- }
461
 
462
- // 设置请求头
463
- youReq.Header = http.Header{
464
- "sec-ch-ua-platform": {"Windows"},
465
- "Cache-Control": {"no-cache"},
466
- "sec-ch-ua": {`"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"`},
467
- "sec-ch-ua-bitness": {"64"},
468
- "sec-ch-ua-model": {""},
469
- "sec-ch-ua-mobile": {"?0"},
470
- "sec-ch-ua-arch": {"x86"},
471
- "sec-ch-ua-full-version": {"133.0.3065.39"},
472
- "Accept": {"text/event-stream"},
473
- "User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0"},
474
- "sec-ch-ua-platform-version": {"19.0.0"},
475
- "Sec-Fetch-Site": {"same-origin"},
476
- "Sec-Fetch-Mode": {"cors"},
477
- "Sec-Fetch-Dest": {"empty"},
478
- "Host": {"you.com"},
479
- }
480
 
481
- // 设置 Cookie
482
- cookies := getCookies(dsToken)
483
- var cookieStrings []string
484
- for name, value := range cookies {
485
- cookieStrings = append(cookieStrings, fmt.Sprintf("%s=%s", name, value))
486
- }
487
- youReq.Header.Add("Cookie", strings.Join(cookieStrings, ";"))
488
- fmt.Printf("Cookie: %s\n", strings.Join(cookieStrings, ";"))
489
- fmt.Printf("===================\n\n")
490
 
491
- // 根据 OpenAI 请求的 stream 参数选择处理函数
492
- if !openAIReq.Stream {
493
- handleNonStreamingResponse(w, youReq) // 处理非流式响应
494
- return
495
- }
 
 
496
 
497
- handleStreamingResponse(w, youReq) // 处理流式响应
 
 
 
498
  }
 
 
499
  }
500
 
501
  // getCookies 根据提供的 DS token 生成所需的 Cookie。
@@ -707,7 +680,7 @@ func countTokens(messages []Message) (int, error) {
707
  }
708
 
709
  // 计算 tokens:英文字符 * 0.3 + 中文字符 * 0.6
710
- tokens := int(float64(englishCount)*0.3 + float64(chineseCount)*0.6)
711
 
712
  // 加上角色名的 token(约 2 个)
713
  totalTokens += tokens + 2
 
24
  }
25
 
26
  const (
27
+ MaxContextTokens = 2000 // 最大上下文 token 数
28
  )
29
 
30
  // YouChatResponse 定义了从 You.com API 接收的单个 token 的结构。
 
240
  // 转换 system 消息为 user 消息
241
  openAIReq.Messages = convertSystemToUser(openAIReq.Messages)
242
 
 
 
 
 
 
 
 
 
243
  // 构建 You.com 聊天历史
244
  var chatHistory []map[string]interface{}
245
+ var sources []map[string]interface{}
246
+ var lastAssistantMessage string
247
+
248
+ // 处理历史消息(不包括最后一条)
249
+ for _, msg := range openAIReq.Messages[:len(openAIReq.Messages)-1] {
250
+ if msg.Role == "user" {
251
+ tokens, err := countTokens([]Message{msg})
252
+ if err != nil {
253
+ http.Error(w, "Failed to count tokens", http.StatusInternalServerError)
254
+ return
255
+ }
256
+
257
+ if tokens > MaxContextTokens {
258
+ // 获取 nonce
259
+ nonceResp, err := getNonce(dsToken)
260
+ if err != nil {
261
+ fmt.Printf("获取 nonce 失败: %v\n", err)
262
+ http.Error(w, "Failed to get nonce", http.StatusInternalServerError)
263
+ return
264
+ }
265
+
266
+ // 创建临时文件
267
+ tempFile := fmt.Sprintf("temp_%s.txt", nonceResp.Uuid)
268
+ if err := os.WriteFile(tempFile, []byte(msg.Content), 0644); err != nil {
269
+ fmt.Printf("创建临时文件失败: %v\n", err)
270
+ http.Error(w, "Failed to create temp file", http.StatusInternalServerError)
271
+ return
272
+ }
273
+ defer os.Remove(tempFile)
274
+
275
+ // 上传文件
276
+ uploadResp, err := uploadFile(dsToken, tempFile)
277
+ if err != nil {
278
+ fmt.Printf("上传文件失败: %v\n", err)
279
+ http.Error(w, "Failed to upload file", http.StatusInternalServerError)
280
+ return
281
+ }
282
+
283
+ // 添加文件源信息
284
+ sources = append(sources, map[string]interface{}{
285
+ "source_type": "user_file",
286
+ "filename": uploadResp.Filename,
287
+ "user_filename": uploadResp.UserFilename,
288
+ "size_bytes": len(msg.Content),
289
+ })
290
+
291
+ // 在历史记录中使用文件引用
292
+ chatHistory = append(chatHistory, map[string]interface{}{
293
+ "question": fmt.Sprintf("Please review the attached file: %s", uploadResp.UserFilename),
294
+ "answer": "",
295
+ })
296
+ } else {
297
+ chatHistory = append(chatHistory, map[string]interface{}{
298
+ "question": msg.Content,
299
+ "answer": "",
300
+ })
301
+ }
302
+ } else if msg.Role == "assistant" {
303
+ // 只保存最后一条 assistant 消息
304
+ lastAssistantMessage = msg.Content
305
  }
 
306
  }
307
 
308
+ // 如果有最后一条 assistant 消息,添加到历史记录中
309
+ if lastAssistantMessage != "" {
310
+ chatHistory = append(chatHistory, map[string]interface{}{
311
+ "question": "",
312
+ "answer": lastAssistantMessage,
313
+ })
314
+ }
315
+
316
+ chatHistoryJSON, _ := json.Marshal(chatHistory)
317
 
318
  // 创建 You.com API 请求
319
  youReq, _ := http.NewRequest("GET", "https://you.com/api/streamingSearch", nil)
 
323
  conversationTurnId := uuid.New().String()
324
  traceId := fmt.Sprintf("%s|%s|%s", chatId, conversationTurnId, time.Now().Format(time.RFC3339))
325
 
326
+ // 处理最后一条消息
327
+ lastMessage := openAIReq.Messages[len(openAIReq.Messages)-1]
328
+ lastMessageTokens, err := countTokens([]Message{lastMessage})
329
+ if err != nil {
330
+ http.Error(w, "Failed to count tokens", http.StatusInternalServerError)
331
+ return
332
+ }
333
+
334
+ // 构建查询参数
335
+ q := youReq.URL.Query()
336
+
337
+ // 设置基本参数
338
+ q.Add("page", "1")
339
+ q.Add("count", "10")
340
+ q.Add("safeSearch", "Off")
341
+ q.Add("mkt", "en-US")
342
+ q.Add("enable_worklow_generation_ux", "true")
343
+ q.Add("domain", "youchat")
344
+ q.Add("use_personalization_extraction", "true")
345
+ q.Add("queryTraceId", chatId)
346
+ q.Add("chatId", chatId)
347
+ q.Add("conversationTurnId", conversationTurnId)
348
+ q.Add("pastChatLength", fmt.Sprintf("%d", len(chatHistory)))
349
+ q.Add("selectedChatMode", "custom")
350
+ q.Add("selectedAiModel", mapModelName(openAIReq.Model))
351
+ q.Add("enable_agent_clarification_questions", "true")
352
+ q.Add("traceId", traceId)
353
+ q.Add("use_nested_youchat_updates", "true")
354
+
355
  // 如果最后一条消息超过限制,使用文件上传
356
  if lastMessageTokens > MaxContextTokens {
357
+ // 获取 nonce
358
  nonceResp, err := getNonce(dsToken)
359
  if err != nil {
360
  fmt.Printf("获取 nonce 失败: %v\n", err)
361
  http.Error(w, "Failed to get nonce", http.StatusInternalServerError)
362
  return
363
  }
 
 
 
 
364
 
365
+ // 创建临时文件
366
  tempFile := fmt.Sprintf("temp_%s.txt", nonceResp.Uuid)
367
+ if err := os.WriteFile(tempFile, []byte(lastMessage.Content), 0644); err != nil {
368
  fmt.Printf("创建临时文件失败: %v\n", err)
369
  http.Error(w, "Failed to create temp file", http.StatusInternalServerError)
370
  return
 
380
  }
381
 
382
  // 添加文件源信息
383
+ sources = append(sources, map[string]interface{}{
384
+ "source_type": "user_file",
385
+ "filename": uploadResp.Filename,
386
+ "user_filename": uploadResp.UserFilename,
387
+ "size_bytes": len(lastMessage.Content),
388
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
 
390
+ // 添加 sources 参数
391
+ sourcesJSON, _ := json.Marshal(sources)
 
 
 
 
 
 
 
 
 
 
 
392
  q.Add("sources", string(sourcesJSON))
 
 
 
 
 
 
393
 
394
+ // 使用文件引用作为查询
395
+ q.Add("q", fmt.Sprintf("Please review the attached file: %s", uploadResp.UserFilename))
396
+ } else {
397
+ // 如果有之前上传的文件,添加 sources
398
+ if len(sources) > 0 {
399
+ sourcesJSON, _ := json.Marshal(sources)
400
+ q.Add("sources", string(sourcesJSON))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
402
+ q.Add("q", lastMessage.Content)
403
+ }
404
 
405
+ q.Add("chat", string(chatHistoryJSON))
406
+ youReq.URL.RawQuery = q.Encode()
407
 
408
+ fmt.Printf("\n=== 完整请求信息 ===\n")
409
+ fmt.Printf("请求 URL: %s\n", youReq.URL.String())
410
+ fmt.Printf("请求头:\n")
411
+ for key, values := range youReq.Header {
412
+ fmt.Printf("%s: %v\n", key, values)
413
+ }
 
 
 
 
 
 
 
414
 
415
+ // 设置请求头
416
+ youReq.Header = http.Header{
417
+ "sec-ch-ua-platform": {"Windows"},
418
+ "Cache-Control": {"no-cache"},
419
+ "sec-ch-ua": {`"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"`},
420
+ "sec-ch-ua-bitness": {"64"},
421
+ "sec-ch-ua-model": {""},
422
+ "sec-ch-ua-mobile": {"?0"},
423
+ "sec-ch-ua-arch": {"x86"},
424
+ "sec-ch-ua-full-version": {"133.0.3065.39"},
425
+ "Accept": {"text/event-stream"},
426
+ "User-Agent": {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0"},
427
+ "sec-ch-ua-platform-version": {"19.0.0"},
428
+ "Sec-Fetch-Site": {"same-origin"},
429
+ "Sec-Fetch-Mode": {"cors"},
430
+ "Sec-Fetch-Dest": {"empty"},
431
+ "Host": {"you.com"},
432
+ }
433
 
434
+ // 设置 Cookie
435
+ cookies := getCookies(dsToken)
436
+ var cookieStrings []string
437
+ for name, value := range cookies {
438
+ cookieStrings = append(cookieStrings, fmt.Sprintf("%s=%s", name, value))
439
+ }
440
+ youReq.Header.Add("Cookie", strings.Join(cookieStrings, ";"))
441
+ fmt.Printf("Cookie: %s\n", strings.Join(cookieStrings, ";"))
442
+ fmt.Printf("===================\n\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
+ // 发送请求并获取响应
445
+ client := &http.Client{}
446
+ resp, err := client.Do(youReq)
447
+ if err != nil {
448
+ fmt.Printf("发送请求失败: %v\n", err)
449
+ http.Error(w, err.Error(), http.StatusInternalServerError)
450
+ return
451
+ }
452
+ defer resp.Body.Close()
 
 
 
 
 
 
 
 
 
453
 
454
+ // 打印响应状态码
455
+ fmt.Printf("响应状态码: %d\n", resp.StatusCode)
 
 
 
 
 
 
 
456
 
457
+ // 如果状态码不是 200,打印响应内容
458
+ if resp.StatusCode != http.StatusOK {
459
+ body, _ := io.ReadAll(resp.Body)
460
+ fmt.Printf("错误响应内容: %s\n", string(body))
461
+ http.Error(w, fmt.Sprintf("API returned status %d", resp.StatusCode), resp.StatusCode)
462
+ return
463
+ }
464
 
465
+ // 根据 OpenAI 请求的 stream 参数选择处理函数
466
+ if !openAIReq.Stream {
467
+ handleNonStreamingResponse(w, youReq) // 处理非流式响应
468
+ return
469
  }
470
+
471
+ handleStreamingResponse(w, youReq) // 处理流式响应
472
  }
473
 
474
  // getCookies 根据提供的 DS token 生成所需的 Cookie。
 
680
  }
681
 
682
  // 计算 tokens:英文字符 * 0.3 + 中文字符 * 0.6
683
+ tokens := int(float64(englishCount)*0.3 + float64(chineseCount)*1)
684
 
685
  // 加上角色名的 token(约 2 个)
686
  totalTokens += tokens + 2