Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -489,7 +489,7 @@ def list_models():
|
|
489 |
app.logger.info("[list_models] 用户请求 /v1/models")
|
490 |
models_list = [
|
491 |
{
|
492 |
-
"id": "
|
493 |
"object": "model",
|
494 |
"created": 1677610602,
|
495 |
"owned_by": "deepseek",
|
@@ -503,14 +503,14 @@ def list_models():
|
|
503 |
"permission": []
|
504 |
},
|
505 |
{
|
506 |
-
"id": "
|
507 |
"object": "model",
|
508 |
"created": 1677610602,
|
509 |
"owned_by": "deepseek",
|
510 |
"permission": []
|
511 |
},
|
512 |
{
|
513 |
-
"id": "deepseek-
|
514 |
"object": "model",
|
515 |
"created": 1677610602,
|
516 |
"owned_by": "deepseek",
|
@@ -525,8 +525,8 @@ def list_models():
|
|
525 |
# ----------------------------------------------------------------------
|
526 |
def messages_prepare(messages: list) -> str:
|
527 |
"""处理消息列表,合并连续相同角色的消息,并添加角色标签:
|
528 |
-
- 对于 assistant 消息,加上 <|Assistant|> 前缀及
|
529 |
-
- 对于 user/system 消息(除第一条外)加上
|
530 |
- 如果消息 content 为数组,则提取其中 type 为 "text" 的部分;
|
531 |
- 最后移除 markdown 图片格式的内容。
|
532 |
"""
|
@@ -555,10 +555,10 @@ def messages_prepare(messages: list) -> str:
|
|
555 |
role = block["role"]
|
556 |
text = block["text"]
|
557 |
if role == "assistant":
|
558 |
-
parts.append(f"<|Assistant|>{text}
|
559 |
elif role in ("user", "system"):
|
560 |
if idx > 0:
|
561 |
-
parts.append(f"
|
562 |
else:
|
563 |
parts.append(text)
|
564 |
else:
|
@@ -601,14 +601,23 @@ def chat_completions():
|
|
601 |
if not model or not messages:
|
602 |
return jsonify({"error": "Request must include 'model' and 'messages'."}), 400
|
603 |
|
604 |
-
#
|
605 |
model_lower = model.lower()
|
606 |
if model_lower in ["deepseek-v3", "deepseek-chat"]:
|
607 |
thinking_enabled = False
|
|
|
608 |
elif model_lower in ["deepseek-r1", "deepseek-reasoner"]:
|
609 |
thinking_enabled = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
610 |
else:
|
611 |
-
return
|
|
|
612 |
|
613 |
# 使用 messages_prepare 函数构造最终 prompt
|
614 |
final_prompt = messages_prepare(messages)
|
@@ -633,7 +642,7 @@ def chat_completions():
|
|
633 |
"prompt": final_prompt,
|
634 |
"ref_file_ids": [],
|
635 |
"thinking_enabled": thinking_enabled,
|
636 |
-
"search_enabled":
|
637 |
}
|
638 |
app.logger.debug(f"[chat_completions] -> {DEEPSEEK_COMPLETION_URL}, payload={payload}")
|
639 |
|
@@ -657,6 +666,7 @@ def chat_completions():
|
|
657 |
final_text = ""
|
658 |
final_thinking = ""
|
659 |
first_chunk_sent = False
|
|
|
660 |
for raw_line in deepseek_resp.iter_lines(chunk_size=512):
|
661 |
try:
|
662 |
line = raw_line.decode("utf-8")
|
@@ -691,6 +701,12 @@ def chat_completions():
|
|
691 |
try:
|
692 |
chunk = json.loads(data_str)
|
693 |
app.logger.debug(f"[sse_stream] 解析到 chunk: {chunk}")
|
|
|
|
|
|
|
|
|
|
|
|
|
694 |
except Exception as e:
|
695 |
app.logger.warning(f"[sse_stream] 无法解析: {data_str}, 错误: {e}")
|
696 |
continue
|
@@ -699,6 +715,8 @@ def chat_completions():
|
|
699 |
delta = choice.get("delta", {})
|
700 |
ctype = delta.get("type")
|
701 |
ctext = delta.get("content", "")
|
|
|
|
|
702 |
if ctype == "thinking":
|
703 |
if thinking_enabled:
|
704 |
final_thinking += ctext
|
@@ -735,6 +753,7 @@ def chat_completions():
|
|
735 |
# 非流式响应处理
|
736 |
think_list = []
|
737 |
text_list = []
|
|
|
738 |
try:
|
739 |
for raw_line in deepseek_resp.iter_lines(chunk_size=512):
|
740 |
try:
|
@@ -751,16 +770,25 @@ def chat_completions():
|
|
751 |
try:
|
752 |
chunk = json.loads(data_str)
|
753 |
app.logger.debug(f"[chat_completions] 非流式 chunk: {chunk}")
|
|
|
|
|
|
|
|
|
|
|
|
|
754 |
except Exception as e:
|
755 |
app.logger.warning(f"[chat_completions] 无法解析: {data_str}, 错误: {e}")
|
756 |
continue
|
757 |
for choice in chunk.get("choices", []):
|
758 |
delta = choice.get("delta", {})
|
759 |
ctype = delta.get("type")
|
|
|
|
|
|
|
760 |
if ctype == "thinking" and thinking_enabled:
|
761 |
-
think_list.append(
|
762 |
elif ctype == "text":
|
763 |
-
text_list.append(
|
764 |
finally:
|
765 |
deepseek_resp.close()
|
766 |
final_reasoning = "".join(think_list)
|
|
|
489 |
app.logger.info("[list_models] 用户请求 /v1/models")
|
490 |
models_list = [
|
491 |
{
|
492 |
+
"id": "deepseek-chat",
|
493 |
"object": "model",
|
494 |
"created": 1677610602,
|
495 |
"owned_by": "deepseek",
|
|
|
503 |
"permission": []
|
504 |
},
|
505 |
{
|
506 |
+
"id": "deepseek-chat-search",
|
507 |
"object": "model",
|
508 |
"created": 1677610602,
|
509 |
"owned_by": "deepseek",
|
510 |
"permission": []
|
511 |
},
|
512 |
{
|
513 |
+
"id": "deepseek-reasoner-search",
|
514 |
"object": "model",
|
515 |
"created": 1677610602,
|
516 |
"owned_by": "deepseek",
|
|
|
525 |
# ----------------------------------------------------------------------
|
526 |
def messages_prepare(messages: list) -> str:
|
527 |
"""处理消息列表,合并连续相同角色的消息,并添加角色标签:
|
528 |
+
- 对于 assistant 消息,加上 <|Assistant|> 前缀及 结束标签;
|
529 |
+
- 对于 user/system 消息(除第一条外)加上 结束标签;
|
530 |
- 如果消息 content 为数组,则提取其中 type 为 "text" 的部分;
|
531 |
- 最后移除 markdown 图片格式的内容。
|
532 |
"""
|
|
|
555 |
role = block["role"]
|
556 |
text = block["text"]
|
557 |
if role == "assistant":
|
558 |
+
parts.append(f"<|Assistant|>{text}")
|
559 |
elif role in ("user", "system"):
|
560 |
if idx > 0:
|
561 |
+
parts.append(f"结束标签")
|
562 |
else:
|
563 |
parts.append(text)
|
564 |
else:
|
|
|
601 |
if not model or not messages:
|
602 |
return jsonify({"error": "Request must include 'model' and 'messages'."}), 400
|
603 |
|
604 |
+
# 判断是否启用"思考"功能(这里根据模型名称判断)
|
605 |
model_lower = model.lower()
|
606 |
if model_lower in ["deepseek-v3", "deepseek-chat"]:
|
607 |
thinking_enabled = False
|
608 |
+
search_enabled = False
|
609 |
elif model_lower in ["deepseek-r1", "deepseek-reasoner"]:
|
610 |
thinking_enabled = True
|
611 |
+
search_enabled = False
|
612 |
+
elif model_lower in ["deepseek-v3-search", "deepseek-chat-search"]:
|
613 |
+
thinking_enabled = False
|
614 |
+
search_enabled = True
|
615 |
+
elif model_lower in ["deepseek-r1-search", "deepseek-reasoner-search"]:
|
616 |
+
thinking_enabled = True
|
617 |
+
search_enabled = True
|
618 |
else:
|
619 |
+
return Response(json.dumps({"error": f"Model '{model}' is not available."}),
|
620 |
+
status=503, mimetype="application/json")
|
621 |
|
622 |
# 使用 messages_prepare 函数构造最终 prompt
|
623 |
final_prompt = messages_prepare(messages)
|
|
|
642 |
"prompt": final_prompt,
|
643 |
"ref_file_ids": [],
|
644 |
"thinking_enabled": thinking_enabled,
|
645 |
+
"search_enabled": search_enabled
|
646 |
}
|
647 |
app.logger.debug(f"[chat_completions] -> {DEEPSEEK_COMPLETION_URL}, payload={payload}")
|
648 |
|
|
|
666 |
final_text = ""
|
667 |
final_thinking = ""
|
668 |
first_chunk_sent = False
|
669 |
+
citation_map = {} # 用于存储引用链接的字典
|
670 |
for raw_line in deepseek_resp.iter_lines(chunk_size=512):
|
671 |
try:
|
672 |
line = raw_line.decode("utf-8")
|
|
|
701 |
try:
|
702 |
chunk = json.loads(data_str)
|
703 |
app.logger.debug(f"[sse_stream] 解析到 chunk: {chunk}")
|
704 |
+
# 处理搜索索引数据
|
705 |
+
if chunk.get("choices", [{}])[0].get("delta", {}).get("type") == "search_index":
|
706 |
+
search_indexes = chunk["choices"][0]["delta"].get("search_indexes", [])
|
707 |
+
for idx in search_indexes:
|
708 |
+
citation_map[str(idx.get("cite_index"))] = idx.get("url", "")
|
709 |
+
continue
|
710 |
except Exception as e:
|
711 |
app.logger.warning(f"[sse_stream] 无法解析: {data_str}, 错误: {e}")
|
712 |
continue
|
|
|
715 |
delta = choice.get("delta", {})
|
716 |
ctype = delta.get("type")
|
717 |
ctext = delta.get("content", "")
|
718 |
+
if search_enabled and ctext.startswith("[citation:"):
|
719 |
+
ctext = ""
|
720 |
if ctype == "thinking":
|
721 |
if thinking_enabled:
|
722 |
final_thinking += ctext
|
|
|
753 |
# 非流式响应处理
|
754 |
think_list = []
|
755 |
text_list = []
|
756 |
+
citation_map = {} # 用于存储引用链接的字典
|
757 |
try:
|
758 |
for raw_line in deepseek_resp.iter_lines(chunk_size=512):
|
759 |
try:
|
|
|
770 |
try:
|
771 |
chunk = json.loads(data_str)
|
772 |
app.logger.debug(f"[chat_completions] 非流式 chunk: {chunk}")
|
773 |
+
# 处理搜索索引数据
|
774 |
+
if chunk.get("choices", [{}])[0].get("delta", {}).get("type") == "search_index":
|
775 |
+
search_indexes = chunk["choices"][0]["delta"].get("search_indexes", [])
|
776 |
+
for idx in search_indexes:
|
777 |
+
citation_map[str(idx.get("cite_index"))] = idx.get("url", "")
|
778 |
+
continue
|
779 |
except Exception as e:
|
780 |
app.logger.warning(f"[chat_completions] 无法解析: {data_str}, 错误: {e}")
|
781 |
continue
|
782 |
for choice in chunk.get("choices", []):
|
783 |
delta = choice.get("delta", {})
|
784 |
ctype = delta.get("type")
|
785 |
+
ctext = delta.get("content", "")
|
786 |
+
if search_enabled and ctext.startswith("[citation:"):
|
787 |
+
ctext = ""
|
788 |
if ctype == "thinking" and thinking_enabled:
|
789 |
+
think_list.append(ctext)
|
790 |
elif ctype == "text":
|
791 |
+
text_list.append(ctext)
|
792 |
finally:
|
793 |
deepseek_resp.close()
|
794 |
final_reasoning = "".join(think_list)
|