v2.0.1:修复推理型模型(Qwen3/DeepSeek reasoning_content)误报未收到输出+连接测试按连通判定+采样失败不中断+流式超时放宽60s/300s
This commit is contained in:
+23
-11
@@ -56,6 +56,8 @@ def _parse_sse_line(line):
|
||||
|
||||
def _metrics(start, first_token_at, end, prompt_tokens, output_tokens,
|
||||
cached_tokens, output_chars, prompt_chars):
|
||||
if first_token_at is None:
|
||||
first_token_at = end # 未收到正文但请求完成(如纯 usage 响应)
|
||||
ttft_ms = (first_token_at - start) * 1000
|
||||
decode_ms = (end - first_token_at) * 1000
|
||||
total_ms = (end - start) * 1000
|
||||
@@ -103,6 +105,7 @@ def stream_openai(cfg, prompt, gen, log, should_stop=None):
|
||||
first_token_at = None
|
||||
output_chars = 0
|
||||
prompt_tokens = output_tokens = cached_tokens = 0
|
||||
event_count = 0
|
||||
resp = None
|
||||
try:
|
||||
while True:
|
||||
@@ -126,13 +129,15 @@ def stream_openai(cfg, prompt, gen, log, should_stop=None):
|
||||
for obj in _iter_json(resp):
|
||||
if should_stop and should_stop():
|
||||
raise StopRequested()
|
||||
event_count += 1
|
||||
if obj.get("choices"):
|
||||
delta = obj["choices"][0].get("delta") or {}
|
||||
text = delta.get("content") or ""
|
||||
if text:
|
||||
# 兼容推理型模型:Qwen3/DeepSeek 思维链在 reasoning_content
|
||||
piece = delta.get("content") or delta.get("reasoning_content") or ""
|
||||
if piece:
|
||||
if first_token_at is None:
|
||||
first_token_at = time.time()
|
||||
output_chars += len(text)
|
||||
output_chars += len(piece)
|
||||
usage = obj.get("usage")
|
||||
if usage:
|
||||
prompt_tokens = usage.get("prompt_tokens") or 0
|
||||
@@ -148,8 +153,8 @@ def stream_openai(cfg, prompt, gen, log, should_stop=None):
|
||||
if resp is not None:
|
||||
resp.close()
|
||||
|
||||
if first_token_at is None:
|
||||
raise ProviderError("未收到任何输出内容")
|
||||
if event_count == 0:
|
||||
raise ProviderError("未收到任何输出内容(HTTP 200 但响应流为空)")
|
||||
end = time.time()
|
||||
return _metrics(start, first_token_at, end, prompt_tokens, output_tokens,
|
||||
cached_tokens, output_chars, len(prompt))
|
||||
@@ -190,6 +195,7 @@ def stream_anthropic(cfg, prompt, gen, log, should_stop=None):
|
||||
first_token_at = None
|
||||
output_chars = 0
|
||||
prompt_tokens = output_tokens = 0
|
||||
event_count = 0
|
||||
resp = None
|
||||
try:
|
||||
if should_stop and should_stop():
|
||||
@@ -204,12 +210,15 @@ def stream_anthropic(cfg, prompt, gen, log, should_stop=None):
|
||||
for obj in _iter_json(resp):
|
||||
if should_stop and should_stop():
|
||||
raise StopRequested()
|
||||
event_count += 1
|
||||
etype = obj.get("type")
|
||||
if etype == "message_start":
|
||||
usage = (obj.get("message") or {}).get("usage") or {}
|
||||
prompt_tokens = usage.get("input_tokens") or 0
|
||||
elif etype == "content_block_delta":
|
||||
text = (obj.get("delta") or {}).get("text") or ""
|
||||
delta = obj.get("delta") or {}
|
||||
# 兼容 extended thinking:thinking 文本也算输出
|
||||
text = delta.get("text") or delta.get("thinking") or ""
|
||||
if text:
|
||||
if first_token_at is None:
|
||||
first_token_at = time.time()
|
||||
@@ -225,8 +234,8 @@ def stream_anthropic(cfg, prompt, gen, log, should_stop=None):
|
||||
if resp is not None:
|
||||
resp.close()
|
||||
|
||||
if first_token_at is None:
|
||||
raise ProviderError("未收到任何输出内容")
|
||||
if event_count == 0:
|
||||
raise ProviderError("未收到任何输出内容(HTTP 200 但响应流为空)")
|
||||
end = time.time()
|
||||
return _metrics(start, first_token_at, end, prompt_tokens, output_tokens,
|
||||
0, output_chars, len(prompt))
|
||||
@@ -252,6 +261,7 @@ def stream_google(cfg, prompt, gen, log, should_stop=None):
|
||||
first_token_at = None
|
||||
output_chars = 0
|
||||
prompt_tokens = output_tokens = cached_tokens = 0
|
||||
event_count = 0
|
||||
resp = None
|
||||
try:
|
||||
if should_stop and should_stop():
|
||||
@@ -266,11 +276,13 @@ def stream_google(cfg, prompt, gen, log, should_stop=None):
|
||||
for obj in _iter_json(resp):
|
||||
if should_stop and should_stop():
|
||||
raise StopRequested()
|
||||
event_count += 1
|
||||
cands = obj.get("candidates") or []
|
||||
if cands:
|
||||
parts = (cands[0].get("content") or {}).get("parts") or []
|
||||
for part in parts:
|
||||
text = part.get("text") or ""
|
||||
# 兼容 thinking 模型:thought 文本也算输出
|
||||
text = part.get("text") or part.get("thought") or ""
|
||||
if text:
|
||||
if first_token_at is None:
|
||||
first_token_at = time.time()
|
||||
@@ -288,8 +300,8 @@ def stream_google(cfg, prompt, gen, log, should_stop=None):
|
||||
if resp is not None:
|
||||
resp.close()
|
||||
|
||||
if first_token_at is None:
|
||||
raise ProviderError("未收到任何输出内容")
|
||||
if event_count == 0:
|
||||
raise ProviderError("未收到任何输出内容(HTTP 200 但响应流为空)")
|
||||
end = time.time()
|
||||
return _metrics(start, first_token_at, end, prompt_tokens, output_tokens,
|
||||
cached_tokens, output_chars, len(prompt))
|
||||
|
||||
Reference in New Issue
Block a user