1515 "X-DashScope-SSE" : "enable" ,
1616}
1717
18- # --- [NEW] MODEL MAPPING ---
19- # 定义模型名称到 URL 路径的精确映射
18+ # --- MODEL MAPPING ---
19+ # Exact mapping from model name to URL path
2020MODEL_PATH_MAP = {
2121 "deepseek-v3" : "deepseek-ai/DeepSeek-V3" ,
2222 "deepseek-v3.1" : "deepseek-ai/DeepSeek-V3.1" ,
2828 "pre-siliconflow/deepseek-r1" : "deepseek-ai/DeepSeek-R1" ,
2929}
3030
31- # --- EXPECTED ERROR MESSAGES (COPIED FROM TABLE) ---
31+ # --- EXPECTED ERROR MESSAGES ---
3232ERR_MSG_TOP_P_TYPE = "<400> InternalError.Algo.InvalidParameter: Input should be a valid number, unable to parse string as a number: parameters.top_p"
3333ERR_MSG_TOP_P_RANGE = (
3434 "<400> InternalError.Algo.InvalidParameter: Range of top_p should be (0.0, 1.0]"
4242
4343# --- HELPERS ---
4444
45+
4546def make_request (payload : Dict [str , Any ], stream : bool = True ) -> requests .Response :
4647 """
4748 Helper to send POST request using the Dynamic Path URL structure.
@@ -57,17 +58,17 @@ def make_request(payload: Dict[str, Any], stream: bool = True) -> requests.Respo
5758 model_path = MODEL_PATH_MAP [raw_model_name ]
5859 url = f"{ BASE_URL_PREFIX } /{ model_path } "
5960
60- # 处理 Headers
61+ # Handle Headers
6162 current_headers = HEADERS .copy ()
6263 if not stream :
6364 current_headers ["Accept" ] = "application/json"
64- # 移除 SSE 标记
65+ # Remove SSE flag
6566 if "X-DashScope-SSE" in current_headers :
6667 del current_headers ["X-DashScope-SSE" ]
6768
68- # 注意: requests.post 的 stream 参数控制是否立即下载响应体,
69- # 但业务层面的流式由 headers 和 json body 决定,这里保持 requests 的 stream= True
70- # 只是为了方便拿到原始响应,或者统一设为 stream 变量也可以。
69+ # Note: requests.post ' stream' parameter controls whether to immediately download
70+ # the response body. We keep it True here to access raw response/iter_lines conveniently,
71+ # regardless of the business logic stream setting.
7172 return requests .post (url , headers = current_headers , json = payload , stream = True )
7273
7374
@@ -131,7 +132,9 @@ def test_conflict_prefix_and_thinking(self):
131132 "parameters" : {"enable_thinking" : True },
132133 }
133134 response = make_request (payload )
134- assert_exact_error (response , "InvalidParameter" , ERR_MSG_PARTIAL_THINKING_CONFLICT )
135+ assert_exact_error (
136+ response , "InvalidParameter" , ERR_MSG_PARTIAL_THINKING_CONFLICT
137+ )
135138
136139 def test_r1_enable_thinking_unsupported (self ):
137140 payload = {
@@ -159,13 +162,24 @@ def test_r1_usage_structure_no_text_tokens(self):
159162 def test_tool_choice_invalid_error_mapping (self ):
160163 payload = {
161164 "model" : "pre-siliconflow/deepseek-r1" ,
162- "input" : {
163- "messages" : [{"role" : "user" , "content" : "Weather?" }]
164- },
165+ "input" : {"messages" : [{"role" : "user" , "content" : "Weather?" }]},
165166 "parameters" : {
166167 "result_format" : "message" ,
167168 "tool_choice" : {"type" : "get_current_weather" },
168- "tools" : [{"type" : "function" , "function" : {"name" : "get_current_weather" , "description" : "Get weather" , "parameters" : {"type" : "object" , "properties" : {"location" : {"type" : "string" }}, "required" : ["location" ]}}}],
169+ "tools" : [
170+ {
171+ "type" : "function" ,
172+ "function" : {
173+ "name" : "get_current_weather" ,
174+ "description" : "Get weather" ,
175+ "parameters" : {
176+ "type" : "object" ,
177+ "properties" : {"location" : {"type" : "string" }},
178+ "required" : ["location" ],
179+ },
180+ },
181+ }
182+ ],
169183 },
170184 }
171185 response = make_request (payload )
@@ -178,7 +192,16 @@ def test_history_tool_call_fix(self):
178192 "messages" : [
179193 {"role" : "system" , "content" : "sys" },
180194 {"role" : "user" , "content" : "user" },
181- {"role" : "assistant" , "tool_calls" : [{"function" : {"arguments" : "{}" , "name" : "fn" }, "id" : "call_1" , "type" : "function" }]},
195+ {
196+ "role" : "assistant" ,
197+ "tool_calls" : [
198+ {
199+ "function" : {"arguments" : "{}" , "name" : "fn" },
200+ "id" : "call_1" ,
201+ "type" : "function" ,
202+ }
203+ ],
204+ },
182205 {"role" : "tool" , "content" : "res" , "tool_call_id" : "call_1" },
183206 ]
184207 },
@@ -212,49 +235,47 @@ def test_stop_parameter_invalid_type(self):
212235
213236 def test_non_streaming_request (self ):
214237 """
215- [Row 16] 验证非流式返回 (incremental_output=false)
216- 使用优化后的 make_request,代码更干净
238+ Verify non-streaming response (incremental_output=false).
217239 """
218240 payload = {
219241 "model" : "pre-siliconflow/deepseek-v3.2" ,
220242 "input" : {"messages" : [{"role" : "user" , "content" : "Say hi" }]},
221- "parameters" : {"incremental_output" : False }, # 显式设置参数
243+ "parameters" : {"incremental_output" : False }, # Explicitly set parameter
222244 }
223245
224- # 调用优化后的 helper,传入 stream=False
225246 response = make_request (payload , stream = False )
226247
227- assert response .status_code == 200 , f"Non-streaming request failed: { response .text } "
248+ assert (
249+ response .status_code == 200
250+ ), f"Non-streaming request failed: { response .text } "
228251 data = response .json ()
229- # 非流式应该直接返回 output 字段,而不是 SSE 的 delta
252+ # Non-streaming should return ' output' field directly, not SSE delta
230253 assert "output" in data , "Non-streaming response missing 'output' field"
231254
232255 def test_stop_behavior_in_reasoning (self ):
233256 """
234- [Updated] 验证 Stop 参数在 Reasoning 模型中的行为:
235- 1. 确认 reasoning_content 能够正常返回 (不被误截断)。
236- 2. 确认 stop 参数作用于正文 ( content),并正确截断。
257+ Verify Stop parameter behavior in Reasoning models:
258+ 1. Ensure reasoning_content is returned (not truncated).
259+ 2. Ensure stop parameter applies to content and truncates correctly.
237260 """
238- # 设置一个具体的 Stop 词,诱导模型在正文中输出它
239261 target_stop_word = "Banana"
240262 payload = {
241263 "model" : "pre-siliconflow/deepseek-r1" ,
242264 "input" : {
243- "messages" : [{
244- "role" : "user" ,
245- "content" : f"Please think about the color of the sun, and then simply output the phrase: 'The sun is like a { target_stop_word } '"
246- }]
265+ "messages" : [
266+ {
267+ "role" : "user" ,
268+ "content" : f"Please think about the color of the sun, and then simply output the phrase: 'The sun is like a { target_stop_word } '" ,
269+ }
270+ ]
247271 },
248- "parameters" : {
249- "stop" : [target_stop_word ],
250- "incremental_output" : True # 开启增量输出,避免内容重复
251- }
272+ "parameters" : {"stop" : [target_stop_word ], "incremental_output" : True },
252273 }
253274
254275 response = make_request (payload )
255276 assert response .status_code == 200
256277
257- # --- SSE 解析逻辑 ---
278+ # --- SSE Parsing Logic ---
258279 collected_reasoning = ""
259280 collected_content = ""
260281 final_finish_reason = None
@@ -263,8 +284,7 @@ def test_stop_behavior_in_reasoning(self):
263284 if not line :
264285 continue
265286
266- # 解码 line
267- line_text = line .decode ('utf-8' )
287+ line_text = line .decode ("utf-8" )
268288
269289 if line_text .startswith ("data:" ):
270290 json_str = line_text [5 :].strip ()
@@ -274,15 +294,14 @@ def test_stop_behavior_in_reasoning(self):
274294 try :
275295 chunk = json .loads (json_str )
276296
277- # --- [修复 1] 适配 DashScope 结构:先取 output ---
297+ # Adapt to DashScope structure: get output first
278298 output = chunk .get ("output" , {})
279299 choices = output .get ("choices" , [])
280300
281301 if not choices :
282302 continue
283303
284- # --- [修复 2] 适配 DashScope 结构:取 message 而非 delta ---
285- # DashScope 在流式传输中也使用 message 字段
304+ # Adapt to DashScope structure: use 'message' instead of 'delta'
286305 message = choices [0 ].get ("message" , {})
287306
288307 if "reasoning_content" in message :
@@ -297,20 +316,26 @@ def test_stop_behavior_in_reasoning(self):
297316 except json .JSONDecodeError :
298317 continue
299318
300- # --- 断言验证 ---
319+ # --- Assertions ---
301320 print (f"\n [DEBUG] Collected Reasoning Length: { len (collected_reasoning )} " )
302321 print (f"[DEBUG] Collected Content: '{ collected_content } '" )
303322 print (f"[DEBUG] Finish Reason: { final_finish_reason } " )
304323
305- # 1. 验证 reasoning 内容回来了 (Think 过程不应为空)
306- assert len (collected_reasoning ) > 10 , f"Expected significant reasoning content from R1 model. { collected_reasoning } "
324+ # 1. Verify reasoning content is present (Think process should not be empty)
325+ assert (
326+ len (collected_reasoning ) > 10
327+ ), f"Expected significant reasoning content from R1 model. { collected_reasoning } "
307328
308- # 2. 验证 Stop 生效 (Content 应该包含 'The sun is like a ' 但不包含 'Banana' )
329+ # 2. Verify Stop worked (Content should include prefix but stop before target word )
309330 assert "The sun" in collected_content
310- assert target_stop_word not in collected_content , f"Content should stop before '{ target_stop_word } '"
311-
312- # 3. 验证 finish_reason 是 stop
313- assert final_finish_reason == "stop" , f"Expected finish_reason to be 'stop', but got '{ final_finish_reason } '"
331+ assert (
332+ target_stop_word not in collected_content
333+ ), f"Content should stop before '{ target_stop_word } '"
334+
335+ # 3. Verify finish_reason is stop
336+ assert (
337+ final_finish_reason == "stop"
338+ ), f"Expected finish_reason to be 'stop', but got '{ final_finish_reason } '"
314339
315340 def test_thinking_budget_behavior (self ):
316341 payload = {
0 commit comments